first init of 0.9

This commit is contained in:
cnjinhao
2014-12-11 03:32:35 +08:00
commit d0a317bd45
206 changed files with 69773 additions and 0 deletions

70
source/any.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include <nana/any.hpp>
namespace nana
{
//class any
//struct super_type
any::super_type::~super_type(){}
any::super_type& any::super_type::operator=(const super_type &rhs)
{
return assign(rhs);
}
//end struct super_type
any::any()
:super_(nullptr)
{}
any::any(const any& rhs)
:super_(rhs.super_ ? rhs.super_->clone() : nullptr)
{}
any::any(any&& r)
:super_(r.super_)
{
r.super_ = nullptr;
}
any::~any()
{
delete super_;
}
any& any::operator=(const any& rhs)
{
if(this != &rhs)
{
delete super_;
super_ = (rhs.super_ ? rhs.super_->clone() : nullptr);
}
return *this;
}
any& any::operator=(any&& r)
{
if(this != &r)
{
delete super_;
super_ = r.super_;
r.super_ = nullptr;
}
return *this;
}
bool any::same(const any &rhs) const
{
if(this != &rhs)
{
if(super_ && rhs.super_)
return super_->same(*rhs.super_);
else if(super_ || rhs.super_)
return false;
}
return true;
}
//end class any
}//end namespace nana

View File

@@ -0,0 +1,237 @@
#include <nana/audio/detail/audio_device.hpp>
#include <nana/system/platform.hpp>
#if defined(NANA_LINUX)
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#endif
namespace nana{namespace audio
{
namespace detail
{
#if defined(NANA_WINDOWS)
class wave_native
{
typedef MMRESULT (__stdcall *out_open_t)(LPHWAVEOUT, UINT_PTR, LPWAVEFORMATEX, DWORD_PTR, DWORD_PTR, DWORD);
typedef MMRESULT (__stdcall *out_close_t)(HWAVEOUT);
typedef MMRESULT (__stdcall *out_op_header_t)(HWAVEOUT, LPWAVEHDR, UINT);
public:
out_open_t out_open;
out_close_t out_close;
out_op_header_t out_write;
out_op_header_t out_prepare;
out_op_header_t out_unprepare;
wave_native()
{
HMODULE winmm = ::GetModuleHandleA("Winmm.DLL");
if(0 == winmm)
winmm = ::LoadLibraryA("Winmm.DLL");
out_open = reinterpret_cast<out_open_t>(::GetProcAddress(winmm, "waveOutOpen"));
out_close = reinterpret_cast<out_close_t>(::GetProcAddress(winmm, "waveOutClose"));
out_write = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutWrite"));
out_prepare = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutPrepareHeader"));
out_unprepare = reinterpret_cast<out_op_header_t>(::GetProcAddress(winmm, "waveOutUnprepareHeader"));
}
}wave_native_if;
#endif
//class audio_device
audio_device::audio_device()
#if defined(NANA_WINDOWS)
: handle_(nullptr), buf_prep_(nullptr)
#elif defined(NANA_LINUX)
: handle_(nullptr), buf_prep_(nullptr)
#endif
{}
audio_device::~audio_device()
{
close();
}
bool audio_device::empty() const
{
return (nullptr == handle_);
}
bool audio_device::open(std::size_t channels, std::size_t rate, std::size_t bits_per_sample)
{
#if defined(NANA_WINDOWS)
close();
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = static_cast<WORD>(channels);
wfx.nSamplesPerSec = static_cast<DWORD>(rate);
wfx.wBitsPerSample = static_cast<WORD>(bits_per_sample);
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3 ) * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
wfx.cbSize = 0;
MMRESULT mmr = wave_native_if.out_open(&handle_, WAVE_MAPPER, &wfx, reinterpret_cast<DWORD_PTR>(&audio_device::_m_dev_callback), reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
return (mmr == MMSYSERR_NOERROR);
#elif defined(NANA_LINUX)
if(nullptr == handle_)
{
if(::snd_pcm_open(&handle_, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0)
return false;
}
if(handle_)
{
channels_ = channels;
rate_ = rate;
bytes_per_sample_ = (bits_per_sample >> 3);
bytes_per_frame_ = bytes_per_sample_ * channels;
snd_pcm_hw_params_t * params;
if(snd_pcm_hw_params_malloc(&params) < 0)
{
close();
return false;
}
if(::snd_pcm_hw_params_any(handle_, params) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
if(::snd_pcm_hw_params_set_access(handle_, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
snd_pcm_format_t format = SND_PCM_FORMAT_U8;
switch(bits_per_sample)
{
case 8:
format = SND_PCM_FORMAT_U8; break;
case 16:
format = SND_PCM_FORMAT_S16_LE; break;
case 32:
format = SND_PCM_FORMAT_S32_LE; break;
}
if(::snd_pcm_hw_params_set_format(handle_, params, format) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
unsigned tmp = rate;
if(::snd_pcm_hw_params_set_rate_near(handle_, params, &tmp, 0) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
if(::snd_pcm_hw_params_set_channels(handle_, params, channels) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
if(::snd_pcm_hw_params(handle_, params) < 0)
{
close();
::snd_pcm_hw_params_free(params);
return false;
}
::snd_pcm_hw_params_free(params);
::snd_pcm_prepare(handle_);
return true;
}
return false;
#endif
}
void audio_device::close()
{
if(handle_)
{
#if defined(NANA_WINDOWS)
wave_native_if.out_close(handle_);
#elif defined(NANA_LINUX)
::snd_pcm_close(handle_);
#endif
handle_ = nullptr;
}
}
void audio_device::prepare(buffer_preparation & buf_prep)
{
buf_prep_ = & buf_prep;
}
void audio_device::write(buffer_preparation::meta * m)
{
#if defined(NANA_WINDOWS)
std::lock_guard<decltype(queue_lock_)> lock(queue_lock_);
done_queue_.push_back(m);
if(m->dwFlags & WHDR_PREPARED)
wave_native_if.out_unprepare(handle_, m, sizeof(WAVEHDR));
wave_native_if.out_prepare(handle_, m, sizeof(WAVEHDR));
wave_native_if.out_write(handle_, m, sizeof(WAVEHDR));
#elif defined(NANA_LINUX)
std::size_t frames = m->bufsize / bytes_per_frame_;
std::size_t buffered = 0; //in bytes
while(frames > 0)
{
int err = ::snd_pcm_writei(handle_, m->buf + buffered, frames);
if(err > 0)
{
frames -= err;
buffered += err * bytes_per_frame_;
}
else if(-EPIPE == err)
::snd_pcm_prepare(handle_);
}
buf_prep_->revert(m);
#endif
}
void audio_device::wait_for_drain() const
{
#if defined(NANA_WINDOWS)
while(buf_prep_->data_finished() == false)
nana::system::sleep(200);
#elif defined(NANA_LINUX)
while(::snd_pcm_state(handle_) == SND_PCM_STATE_RUNNING)
nana::system::sleep(200);
#endif
}
#if defined(NANA_WINDOWS)
void __stdcall audio_device::_m_dev_callback(HWAVEOUT handle, UINT msg, audio_device * self, DWORD_PTR, DWORD_PTR)
{
if(WOM_DONE == msg)
{
buffer_preparation::meta * m;
{
std::lock_guard<decltype(queue_lock_)> lock(self->queue_lock_);
m = self->done_queue_.front();
self->done_queue_.erase(self->done_queue_.begin());
}
wave_native_if.out_unprepare(handle, m, sizeof(WAVEHDR));
self->buf_prep_->revert(m);
}
}
#endif
//end class audio_device
}//end namespace detail
}//end namespace audio
}//end namespace nana

View File

@@ -0,0 +1,91 @@
#include <nana/audio/detail/audio_stream.hpp>
#include <nana/charset.hpp>
namespace nana{ namespace audio
{
namespace detail
{
//class audio_stream
bool audio_stream::open(const nana::string& file)
{
fs_.open(static_cast<std::string>(nana::charset(file)), std::ios::binary);
if(fs_)
{
wave_spec::master_riff_chunk riff;
fs_.read(reinterpret_cast<char*>(&riff), sizeof(riff));
if(riff.ckID == *reinterpret_cast<const unsigned long*>("RIFF") && riff.waveID == *reinterpret_cast<const unsigned long*>("WAVE"))
{
fs_.read(reinterpret_cast<char*>(&ck_format_), sizeof(ck_format_));
if(ck_format_.ckID == *reinterpret_cast<const unsigned long*>("fmt ") && ck_format_.wFormatTag == 1) //Only support PCM format
{
std::size_t cksize = _m_locate_chunck(*reinterpret_cast<const unsigned long*>("data"));
if(cksize)
{
pcm_data_pos_ = static_cast<std::size_t>(fs_.tellg());
pcm_data_size_ = cksize;
return true;
}
}
}
}
return false;
}
void audio_stream::close()
{
fs_.close();
}
bool audio_stream::empty() const
{
return (!fs_);
}
const wave_spec::format_chunck & audio_stream::format() const
{
return ck_format_;
}
std::size_t audio_stream::data_length() const
{
return data_size_;
}
void audio_stream::locate()
{
fs_.clear();
fs_.seekg(pcm_data_pos_, std::ios::beg);
data_size_ = pcm_data_size_;
}
std::size_t audio_stream::read(void * buf, std::size_t len)
{
fs_.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len <= data_size_ ? len : data_size_));
std::size_t read_bytes = static_cast<std::size_t>(fs_.gcount());
data_size_ -= read_bytes;
return read_bytes;
}
std::size_t audio_stream::_m_locate_chunck(unsigned ckID)
{
chunck ck;
while(true)
{
fs_.read(reinterpret_cast<char*>(&ck), sizeof(ck));
if(fs_.gcount() != sizeof(ck))
break;
if(ck.ckID == ckID)
return ck.cksize;
if(ck.ckID == *reinterpret_cast<const unsigned long*>("data"))
fs_.seekg(ck.cksize + (ck.cksize & 1 ? 1 : 0), std::ios::cur);
else
fs_.seekg(ck.cksize, std::ios::cur);
}
return 0;
}
//end class audio_stream
}//end namespace detail
}//end namespace audio
}//end namespace nana

View File

@@ -0,0 +1,161 @@
#include <nana/audio/detail/buffer_preparation.hpp>
#include <cstring>
namespace nana{ namespace audio
{
namespace detail
{
//class buffer_preparation
buffer_preparation::buffer_preparation(audio_stream& as, std::size_t seconds)
: running_(true), wait_for_buffer_(false), as_(as)
{
//Allocate the space
buffer_.reserve(seconds);
prepared_.reserve(seconds);
const wave_spec::format_chunck & ck = as.format();
block_size_ = ck.nAvgBytesPerSec;
for(std::size_t i = 0; i < seconds; ++i)
{
char * rawbuf = new char[sizeof(meta) + ck.nAvgBytesPerSec];
meta * m = reinterpret_cast<meta*>(rawbuf);
#if defined(NANA_WINDOWS)
memset(m, 0, sizeof(meta));
m->dwBufferLength = static_cast<unsigned long>(block_size_);
m->lpData = rawbuf + sizeof(meta);
#elif defined(NANA_LINUX)
m->bufsize = ck.nAvgBytesPerSec;
m->buf = rawbuf + sizeof(meta);
#endif
prepared_.push_back(m);
}
thr_ = std::move(std::thread([this](){this->_m_prepare_routine();}));
}
buffer_preparation::~buffer_preparation()
{
running_ = false;
cond_prepared_.notify_one();
cond_buffer_.notify_one();
if(thr_.joinable())
thr_.join();
for(auto metaptr : prepared_)
delete [] reinterpret_cast<char*>(metaptr);
}
buffer_preparation::meta * buffer_preparation::read()
{
std::unique_lock<decltype(token_buffer_)> lock(token_buffer_);
//Wait for the buffer
if(0 == buffer_.size())
{
//Before waiting, checks the thread whether it is finished
//it indicates the preparation is finished.
if(false == running_)
return nullptr;
wait_for_buffer_ = true;
cond_buffer_.wait(lock);
//NO more buffers
if(0 == buffer_.size())
return nullptr;
}
meta * m = buffer_.front();
buffer_.erase(buffer_.begin());
return m;
}
//Revert the meta that returned by read()
void buffer_preparation::revert(meta * m)
{
std::lock_guard<decltype(token_prepared_)> lock(token_prepared_);
bool if_signal = prepared_.empty();
prepared_.push_back(m);
if(if_signal)
cond_prepared_.notify_one();
}
bool buffer_preparation::data_finished() const
{
std::lock_guard<decltype(token_prepared_)> lock(token_prepared_);
return ((prepared_.size() == prepared_.capacity()) && (running_ == false));
}
void buffer_preparation::_m_prepare_routine()
{
const std::size_t fixed_bufsize = 1024;
char buf[fixed_bufsize];
const std::size_t block_size = block_size_;
while(running_)
{
meta * m = 0;
{
std::unique_lock<decltype(token_prepared_)> lock(token_prepared_);
if(prepared_.size() == 0)
{
cond_prepared_.wait(lock);
if(false == running_)
return;
}
m = prepared_.back();
prepared_.pop_back();
}
std::size_t buffered = 0;
while(buffered != block_size)
{
std::size_t want_bytes = block_size - buffered;
if(want_bytes > fixed_bufsize) want_bytes = fixed_bufsize;
std::size_t read_bytes = as_.read(buf, want_bytes);
if(read_bytes)
{
#if defined(NANA_WINDOWS)
memcpy(m->lpData + buffered, buf, read_bytes);
#elif defined(NANA_LINUX)
memcpy(m->buf + buffered, buf, read_bytes);
#endif
buffered += read_bytes;
}
else if(0 == as_.data_length())
{
if(buffered == 0)
{
//PCM data is drained
std::lock_guard<decltype(token_buffer_)> lock(token_buffer_);
cond_buffer_.notify_one();
running_ = false;
return;
}
break;
}
}
#if defined(NANA_WINDOWS)
m->dwBufferLength = static_cast<unsigned long>(buffered);
#elif defined(NANA_LINUX)
m->bufsize = buffered;
#endif
std::lock_guard<decltype(token_buffer_)> lock(token_buffer_);
buffer_.push_back(m);
if(wait_for_buffer_)
{
cond_buffer_.notify_one();
wait_for_buffer_ = false;
}
if(0 == as_.data_length())
running_ = false;
}
}
//end class buffer_preparation
}//end namespace detail
}//end namespace audio
}//end namespace nana

65
source/audio/player.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include <nana/audio/player.hpp>
#include <nana/audio/detail/audio_stream.hpp>
#include <nana/audio/detail/audio_device.hpp>
#include <nana/audio/detail/buffer_preparation.hpp>
#include <nana/system/platform.hpp>
namespace nana{ namespace audio
{
//class player
struct player::implementation
{
detail::audio_stream stream;
detail::audio_device dev;
};
player::player()
: impl_(new implementation)
{}
player::player(const nana::string& file)
: impl_(new implementation)
{
open(file);
}
player::~player()
{
delete impl_;
}
bool player::open(const nana::string& file)
{
if(impl_->stream.open(file))
{
const detail::wave_spec::format_chunck & ck = impl_->stream.format();
return impl_->dev.open(ck.nChannels, ck.nSamplePerSec, ck.wBitsPerSample);
}
return false;
}
void player::play()
{
if(impl_->dev.empty() || impl_->stream.empty()) return;
//Locate the PCM
impl_->stream.locate();
const std::size_t seconds = 5;
detail::buffer_preparation buffer(impl_->stream, seconds);
impl_->dev.prepare(buffer);
detail::buffer_preparation::meta * meta;
while((meta = buffer.read()))
{
impl_->dev.write(meta);
}
impl_->dev.wait_for_drain();
}
void player::close()
{
impl_->dev.close();
impl_->stream.close();
}
}//end namespace audio
}//end namespace nana

313
source/basic_types.cpp Normal file
View File

@@ -0,0 +1,313 @@
/*
* Basic Types definition
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/basic_types.cpp
*/
#include <nana/basic_types.hpp>
namespace nana
{
//struct point
point::point():x(0), y(0){}
point::point(int x, int y):x(x), y(y){}
point::point(const rectangle& r)
: x(r.x), y(r.y)
{}
point& point::operator=(const rectangle& r)
{
x = r.x;
y = r.y;
return *this;
}
bool point::operator==(const point& rhs) const
{
return ((x == rhs.x) && (y == rhs.y));
}
bool point::operator!=(const point& rhs) const
{
return ((x != rhs.x) || (y != rhs.y));
}
bool point::operator<(const point& rhs) const
{
return ((y < rhs.y) || (y == rhs.y && x < rhs.x));
}
bool point::operator<=(const point& rhs) const
{
return ((y < rhs.y) || (y == rhs.y && x <= rhs.x));
}
bool point::operator>(const point& rhs) const
{
return ((y > rhs.y) || (y == rhs.y && x > rhs.x));
}
bool point::operator>=(const point& rhs) const
{
return ((y > rhs.y) || (y == rhs.y && x >= rhs.x));
}
point point::operator-(const point& rhs) const
{
return{x - rhs.x, y - rhs.y};
}
point point::operator+(const point& rhs) const
{
return{ x + rhs.x, y + rhs.y };
}
point& point::operator-=(const point& rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
point& point::operator+=(const point& rhs)
{
x += rhs.x;
y += rhs.y;
return *this;
}
//end struct point
//struct upoint
upoint::upoint():x(0), y(0){}
upoint::upoint(unsigned x, unsigned y):x(x), y(y){}
bool upoint::operator==(const upoint& rhs) const
{
return ((x == rhs.x) && (y == rhs.y));
}
bool upoint::operator!=(const upoint& rhs) const
{
return ((x != rhs.x) || (y != rhs.y));
}
bool upoint::operator<(const upoint& rhs) const
{
return ((y < rhs.y) || (y == rhs.y && x < rhs.x));
}
bool upoint::operator<=(const upoint& rhs) const
{
return ((y < rhs.y) || (y == rhs.y && x <= rhs.x));
}
bool upoint::operator>(const upoint& rhs) const
{
return ((y > rhs.y) || (y == rhs.y && x > rhs.x));
}
bool upoint::operator>=(const upoint& rhs) const
{
return ((y > rhs.y) || (y == rhs.y && x >= rhs.x));
}
//end struct upoint
//struct size
size::size():width(0), height(0){}
size::size(unsigned width, unsigned height):width(width), height(height){}
size::size(const rectangle& r)
: width(r.width), height(r.height)
{}
size& size::operator=(const rectangle& r)
{
width = r.width;
height = r.height;
return *this;
}
bool size::empty() const
{
return (0 == width || 0 == height);
}
bool size::is_hit(const point& pos) const
{
return (0 <= pos.x && pos.x < static_cast<int>(width) && 0 <= pos.y && pos.y < static_cast<int>(height));
}
bool size::operator==(const size& rhs) const
{
return (width == rhs.width) && (height == rhs.height);
}
bool size::operator!=(const size& rhs) const
{
return (width != rhs.width) || (height != rhs.height);
}
size size::operator+(const size& sz) const
{
return{width + sz.width, height + sz.height};
}
//end struct size
//struct rectangle
rectangle::rectangle()
:x(0), y(0), width(0), height(0)
{}
rectangle::rectangle(int x, int y, unsigned width, unsigned height)
:x(x), y(y), width(width), height(height)
{}
rectangle::rectangle(const size & sz)
:x(0), y(0), width(sz.width), height(sz.height)
{}
rectangle::rectangle(const point & pos, const size& sz)
: x(pos.x), y(pos.y), width(sz.width), height(sz.height)
{}
bool rectangle::operator==(const rectangle& rhs) const
{
return (width == rhs.width) && (height == rhs.height) && (x == rhs.x) && (y == rhs.y);
}
bool rectangle::operator!=(const rectangle& rhs) const
{
return (width != rhs.width) || (height != rhs.height) || (x != rhs.x) || (y != rhs.y);
}
rectangle & rectangle::operator=(const point& pos)
{
x = pos.x;
y = pos.y;
return *this;
}
rectangle & rectangle::operator=(const size & sz)
{
width = sz.width;
height = sz.height;
return *this;
}
rectangle& rectangle::set_pos(const point& pos)
{
x = pos.x;
y = pos.y;
return *this;
}
rectangle& rectangle::set_size(const size& sz)
{
width = sz.width;
height = sz.height;
return *this;
}
rectangle& rectangle::pare_off(int pixels)
{
x += pixels;
y += pixels;
width -= (pixels << 1);
height -= (pixels << 1);
return *this;
}
int rectangle::right() const
{
return static_cast<int>(x + width);
}
int rectangle::bottom() const
{
return static_cast<int>(y + height);
}
bool rectangle::is_hit(int pos_x, int pos_y) const
{
return (x <= pos_x && pos_x < x + static_cast<int>(width)) &&
(y <= pos_y && pos_y < y + static_cast<int>(height));
}
bool rectangle::is_hit(const point& pos) const
{
return (x <= pos.x && pos.x < x + static_cast<int>(width)) &&
(y <= pos.y && pos.y < y + static_cast<int>(height));
}
bool rectangle::empty() const
{
return (0 == width) || (0 == height);
}
//end struct rectangle
//class area_rotator
area_rotator::area_rotator(bool rotated, const ::nana::rectangle& area)
: rotated_(rotated),
area_(area)
{}
int area_rotator::x() const
{
return (rotated_ ? area_.y : area_.x);
}
int & area_rotator::x_ref()
{
return (rotated_ ? area_.y : area_.x);
}
int area_rotator::y() const
{
return (rotated_ ? area_.x : area_.y);
}
int & area_rotator::y_ref()
{
return (rotated_ ? area_.x : area_.y);
}
unsigned area_rotator::w() const
{
return (rotated_ ? area_.height : area_.width);
}
unsigned & area_rotator::w_ref()
{
return (rotated_ ? area_.height : area_.width);
}
unsigned area_rotator::h() const
{
return (rotated_ ? area_.width : area_.height);
}
unsigned & area_rotator::h_ref()
{
return (rotated_ ? area_.width : area_.height);
}
int area_rotator::right() const
{
return (rotated_ ? area_.y + static_cast<int>(area_.height) : area_.x + static_cast<int>(area_.width));
}
int area_rotator::bottom() const
{
return (rotated_ ? area_.x + static_cast<int>(area_.width) : area_.y + static_cast<int>(area_.height));
}
const ::nana::rectangle& area_rotator::result() const
{
return area_;
}
//end class area_rotator
}

1071
source/charset.cpp Normal file

File diff suppressed because it is too large Load Diff

274
source/datetime.cpp Normal file
View File

@@ -0,0 +1,274 @@
/*
* A Date Time Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/datetime.cpp
*/
#include <nana/config.hpp>
#include <nana/datetime.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#endif
namespace nana
{
//class date
date::date()
{
time_t t = std::time(nullptr);
struct tm * tm_addr = std::localtime(&t);
value_.year = tm_addr->tm_year + 1900;
value_.month = tm_addr->tm_mon + 1;
value_.day = tm_addr->tm_mday;
}
date::date(const std::tm& t)
{
value_.year = t.tm_year + 1900;
value_.month = t.tm_mon + 1;
value_.day = t.tm_mday;
}
date::date(int year, int month, int day)
{
if(1601 <= year && year < 30827 && 0 < month && month < 13 && day > 0)
{
if(day <= static_cast<int>(date::month_days(year, month)))
{
value_.year = year;
value_.month = month;
value_.day = day;
return;
}
}
time_t t = std::time(0);
struct tm * tm_addr = std::localtime(&t);
value_.year = tm_addr->tm_year + 1900;
value_.month = tm_addr->tm_mon + 1;
value_.day = tm_addr->tm_mday;
}
date date::operator - (int off) const
{
if(off < 0)
return _m_add(static_cast<unsigned>(-off));
return _m_sub(static_cast<unsigned>(off));
}
date date::operator + (int off) const
{
if(off < 0)
return _m_sub(static_cast<unsigned>(-off));
return _m_add(static_cast<unsigned>(off));
}
bool date::operator==(const date& r) const
{
return (r.value_.year == value_.year && r.value_.month == value_.month && r.value_.day == value_.day);
}
bool date::operator!=(const date& r) const
{
return ! (this->operator==(r));
}
bool date::operator <(const date & r) const
{
if(value_.year < r.value_.year)
{
return true;
}
else if(value_.year == r.value_.year)
{
if(value_.month < r.value_.month)
return true;
else if(value_.month == r.value_.month)
return (value_.day < r.value_.day);
}
return false;
}
bool date::operator>(const date & r) const
{
if(value_.year > r.value_.year)
{
return true;
}
else if(value_.year == r.value_.year)
{
if(value_.month > r.value_.month)
return true;
else if(value_.month == r.value_.month)
return (value_.day > r.value_.day);
}
return false;
}
bool date::operator<=(const date& r) const
{
return !(this->operator>(r));
}
bool date::operator>=(const date& r) const
{
return !(this->operator<(r));
}
int date::day_of_week() const
{
return day_of_week(static_cast<int>(value_.year), static_cast<int>(value_.month), static_cast<int>(value_.day));
}
int date::day_of_week(int year, int month, int day)
{
//w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1;
//the Jan and Feb of Every year are treated as the 13th/14th month of last year.
if(month < 3)
{
month += 12;
--year;
}
int century = year / 100;
year = year % 100;
int w = year + (year / 4) + (century / 4) - (2 * century) + (26 * ( month + 1) / 10) + day - 1;
if(w >= 0)
return (w % 7);
return ((1 - (w / 7)) * 7 + w);
}
const date::value & date::read() const
{
return this->value_;
}
date date::_m_add(unsigned x) const
{
date d(*this);
while(x)
{
unsigned off = month_days(d.value_.year, d.value_.month) - d.value_.day;
if(off < x)
{
d.value_.day = 1;
if(d.value_.month == 12)
{
d.value_.month = 1;
++ d.value_.year;
}
else
++ d.value_.month;
x -= (off + 1);
}
else if(off >= x)
{
d.value_.day += x;
break;
}
}
return d;
}
date date::_m_sub(unsigned x) const
{
date d(*this);
while(x)
{
if(d.value_.day <= x)
{
if(d.value_.month == 1)
{
d.value_.month = 12;
-- d.value_.year;
}
else
-- d.value_.month;
d.value_.day = month_days(d.value_.year, d.value_.month);
x -= d.value_.day;
}
else
{
d.value_.day -= x;
break;
}
}
return d;
}
unsigned date::day_in_year(unsigned y, unsigned m, unsigned d)
{
unsigned days = 0;
for(unsigned i = 1; i < m; ++i)
days += month_days(y, i);
return days + d;
}
unsigned date::month_days(unsigned year, unsigned month)
{
unsigned num[] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if(month != 2)
return num[month - 1];
if(((year % 4 == 0) && (year % 100)) || (year % 400 == 0))
return 29;
return 28;
}
unsigned date::year_days(unsigned year)
{
if(((year % 4 == 0) && (year % 100)) || (year % 400 == 0))
return 366;
return 365;
}
//end class date
//class time
time::time()
{
time_t t = ::time(0);
struct tm * tm_addr = ::localtime(&t);
value_.hour = tm_addr->tm_hour;
value_.minute = tm_addr->tm_min;
value_.second = tm_addr->tm_sec;
}
time::time(const std::tm& t)
{
value_.hour = t.tm_hour;
value_.minute = t.tm_min;
value_.second = t.tm_sec;
}
time::time(unsigned hour, unsigned minute, unsigned second)
{
if(hour < 24 && minute < 60 && second < 62)
{
value_.hour = hour;
value_.minute = minute;
value_.second = second;
}
time_t t = ::time(0);
struct tm * tm_addr = ::localtime(&t);
value_.hour = tm_addr->tm_hour;
value_.minute = tm_addr->tm_min;
value_.second = tm_addr->tm_sec;
}
const time::value& time::read() const
{
return value_;
}
//end class time
}//end namespace nana

73
source/deploy.cpp Normal file
View File

@@ -0,0 +1,73 @@
/*
* The Deploy Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/depoly.cpp
*
* What follow are dependented on what defined in nana/config.hpp
*/
#include <nana/deploy.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_LINUX)
#include <string.h>
#include PLATFORM_SPEC_HPP
#endif
namespace nana
{
std::size_t strlen(const char_t* str)
{
#if defined(NANA_UNICODE)
return ::wcslen(str);
#else
return ::strlen(str);
#endif
}
double strtod(const char_t* str, char_t** endptr)
{
#if defined(NANA_UNICODE)
return ::wcstod(str, endptr);
#else
return ::strtod(str, endptr);
#endif
}
char_t* strcpy(char_t* dest, const char_t* source)
{
#if defined(NANA_UNICODE)
return ::wcscpy(dest, source);
#else
return ::strcpy(dest, source);
#endif
}
bool is_incomplete(const nana::string& str, unsigned pos)
{
#ifndef NANA_UNICODE
if(pos > str.size())
pos = static_cast<unsigned>(str.size());
const nana::char_t * pstr = str.c_str();
if(pstr[pos] < 0)
{
bool incomp = false;
for(unsigned i = 0; i < pos; ++i)
{
if(pstr[i] < 0)
incomp = !incomp;
else
incomp = false;
}
return incomp;
}
#endif
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
/*
* Platform Specification Selector
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Nana Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://nanapro.org/LICENSE_1_0.txt)
*
* @file: nana/detail/platform_spec_selector.cpp
*
* This file is used to support the Nana project of some cross-platform IDE,
*
*/
#include <nana/config.hpp>
#if defined(NANA_WINDOWS)
#include "win32/platform_spec.cpp"
#elif defined(NANA_LINUX)
#include "linux_X11/platform_spec.cpp"
#endif

View File

@@ -0,0 +1,246 @@
/*
* Platform Specification Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/detail/platform_spec.cpp
*
* This file provides basis class and data structrue that required by nana
*/
#include <nana/config.hpp>
#include PLATFORM_SPEC_HPP
#include <shellapi.h>
#include <stdexcept>
namespace nana
{
namespace detail
{
drawable_impl_type::drawable_impl_type()
: pixbuf_ptr(nullptr), bytes_per_line(0),
fgcolor_(0xFFFFFFFF)
{
pen.handle = nullptr;
pen.color = nana::null_color;
pen.style = -1;
pen.width = -1;
brush.handle = nullptr;
brush.style = brush_spec::Solid;
brush.color = nana::null_color;
round_region.handle = nullptr;
round_region.radius_x = round_region.radius_y = 0;
string.tab_length = 4;
string.tab_pixels = 0;
string.whitespace_pixels = 0;
}
drawable_impl_type::~drawable_impl_type()
{
::DeleteDC(context);
::DeleteObject(pixmap);
::DeleteObject(pen.handle);
::DeleteObject(brush.handle);
::DeleteObject(round_region.handle);
}
void drawable_impl_type::fgcolor(nana::color_t col)
{
if(this->fgcolor_ != col)
{
::SetTextColor(context, NANA_RGB(col));
fgcolor_ = col;
}
}
void drawable_impl_type::pen_spec::set(HDC context, int style, int width, nana::color_t color)
{
if(this->color != color || this->width != width || this->style != style)
{
this->color = color;
this->width = width;
this->style = style;
this->handle = ::CreatePen(style, width, NANA_RGB(color));
::DeleteObject(::SelectObject(context, this->handle));
}
}
void drawable_impl_type::brush_spec::set(HDC context, drawable_impl_type::brush_spec::t style, nana::color_t color)
{
if(this->color != color || this->style != style)
{
this->color = color;
this->style = style;
switch(style)
{
case brush_spec::HatchBDiagonal:
this->handle = ::CreateHatchBrush(HS_BDIAGONAL, NANA_RGB(color));
break;
case brush_spec::Solid:
default:
this->style = brush_spec::Solid;
this->handle = ::CreateSolidBrush(NANA_RGB(color));
break;
}
::DeleteObject(::SelectObject(context, this->handle));
}
}
void drawable_impl_type::round_region_spec::set(const nana::rectangle& r, unsigned radius_x, unsigned radius_y)
{
if(this->r != r || this->radius_x != radius_x || this->radius_y != radius_y)
{
if(handle)
::DeleteObject(this->handle);
this->r = r;
this->radius_x = radius_x;
this->radius_y = radius_y;
handle = ::CreateRoundRectRgn(r.x, r.y, r.x + static_cast<int>(r.width) + 1, r.y + static_cast<int>(r.height) + 1, static_cast<int>(radius_x + 1), static_cast<int>(radius_y + 1));
}
}
//struct font_tag::deleter
void font_tag::deleter::operator()(const font_tag* tag) const
{
if(tag && tag->handle)
::DeleteObject(tag->handle);
delete tag;
}
//end struct font_tag::deleter
//class platform_spec
platform_spec::co_initializer::co_initializer()
: ole32_(::LoadLibrary(STR("OLE32.DLL")))
{
if(ole32_)
{
typedef HRESULT (__stdcall *CoInitializeEx_t)(LPVOID, DWORD);
CoInitializeEx_t fn_init = reinterpret_cast<CoInitializeEx_t>(::GetProcAddress(ole32_, "CoInitializeEx"));
if(0 == fn_init)
{
::FreeLibrary(ole32_);
ole32_ = 0;
throw std::runtime_error("Nana.PlatformSpec.Co_initializer: Can't locate the CoInitializeEx().");
}
else
fn_init(0, COINIT_APARTMENTTHREADED | /*COINIT_DISABLE_OLE1DDE =*/0x4);
}
else
throw std::runtime_error("Nana.PlatformSpec.Co_initializer: No Ole32.DLL Loaded.");
}
platform_spec::co_initializer::~co_initializer()
{
if(ole32_)
{
typedef void (__stdcall *CoUninitialize_t)(void);
CoUninitialize_t fn_unin = reinterpret_cast<CoUninitialize_t>(::GetProcAddress(ole32_, "CoUninitialize"));
if(fn_unin)
fn_unin();
::FreeLibrary(ole32_);
}
}
platform_spec::platform_spec()
{
//Create default font object.
NONCLIENTMETRICS metrics = {};
metrics.cbSize = sizeof metrics;
#if(WINVER >= 0x0600)
OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(osvi);
::GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 6)
metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth);
#endif
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof metrics, &metrics, 0);
def_font_ptr_ = make_native_font(metrics.lfMessageFont.lfFaceName, font_size_to_height(9), 400, false, false, false);
}
const platform_spec::font_ptr_t& platform_spec::default_native_font() const
{
return def_font_ptr_;
}
void platform_spec::default_native_font(const font_ptr_t& fp)
{
def_font_ptr_ = fp;
}
unsigned platform_spec::font_size_to_height(unsigned size) const
{
HDC hdc = ::GetDC(0);
size = ::MulDiv(int(size), ::GetDeviceCaps(hdc, LOGPIXELSY), 72);
::ReleaseDC(0, hdc);
return size;
}
unsigned platform_spec::font_height_to_size(unsigned height) const
{
HDC hdc = ::GetDC(0);
unsigned pixels = ::GetDeviceCaps(hdc, LOGPIXELSY);
::ReleaseDC(0, hdc);
height = static_cast<unsigned>(static_cast<long long>(72) * height / pixels);
return height;
}
platform_spec::font_ptr_t platform_spec::make_native_font(const nana::char_t* name, unsigned height, unsigned weight, bool italic, bool underline, bool strike_out)
{
::LOGFONT logfont;
memset(&logfont, 0, sizeof logfont);
strcpy(logfont.lfFaceName, (name && *name ? name : def_font_ptr_->name.c_str()));
logfont.lfCharSet = DEFAULT_CHARSET;
HDC hdc = ::GetDC(0);
logfont.lfHeight = -static_cast<int>(height);
::ReleaseDC(0, hdc);
logfont.lfWidth = 0;
logfont.lfWeight = weight;
logfont.lfQuality = PROOF_QUALITY;
logfont.lfPitchAndFamily = FIXED_PITCH;
logfont.lfItalic = italic;
logfont.lfUnderline = underline;
logfont.lfStrikeOut = strike_out;
HFONT result = ::CreateFontIndirect(&logfont);
if(result)
{
font_tag * impl = new font_tag;
impl->name = logfont.lfFaceName;
impl->height = height;
impl->weight = weight;
impl->italic = italic;
impl->underline = underline;
impl->strikeout = strike_out;
impl->handle = result;
return std::shared_ptr<font_tag>(impl, font_tag::deleter());
}
return nullptr;
}
platform_spec& platform_spec::instance()
{
static platform_spec object;
return object;
}
void platform_spec::keep_window_icon(native_window_type wd, const paint::image& img)
{
iconbase_[wd] = img;
}
void platform_spec::release_window_icon(native_window_type wd)
{
iconbase_.erase(wd);
}
}//end namespace detail
}//end namespace nana

71
source/exceptions.cpp Normal file
View File

@@ -0,0 +1,71 @@
/*
* Exception Definition
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/exceptions.cpp
*/
#include <nana/exceptions.hpp>
namespace nana
{
//class thrd_exit
thrd_exit::thrd_exit(unsigned retval):retval_(retval){}
thrd_exit::~thrd_exit() throw(){}
const char* thrd_exit::what() const throw(){ return "Exit-Threading Exception"; }
unsigned thrd_exit::retval() const { return retval_; }
//end class thrd_exit
//class bad_member
bad_member::bad_member(const std::string& what):what_(what){}
bad_member::~bad_member() throw(){}
const char* bad_member::what() const throw()
{
return what_.c_str();
}
//end class bad_member
//class bad_syntax
bad_syntax::bad_syntax(const std::string& what):what_(what){}
bad_syntax::~bad_syntax() throw(){}
const char* bad_syntax::what() const throw()
{
return what_.c_str();
}
//end class bad_syntax
//class bad_error
bad_error::bad_error(const std::string& what):what_(what){}
bad_error::~bad_error() throw(){}
const char* bad_error::what() const throw()
{
return what_.c_str();
}
//end class bad_error
//class bad_handle: public std::exception
bad_handle::bad_handle(const std::string& what):what_(what){}
bad_handle::~bad_handle() throw(){}
const char* bad_handle::what() const throw()
{
return what_.c_str();
}
//end class bad_handle
//class bad_window
bad_window::bad_window(const char* what)
:what_(what)
{}
bad_window::~bad_window() throw(){}
const char* bad_window::what() const throw()
{
return what_.c_str();
}
//end class bad_window
} //end namespace nana

View File

@@ -0,0 +1,41 @@
/*
* A File Iterator Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/filesystem/file_iterator.cpp
* @description:
* file_iterator is a toolkit for applying each file and directory in a
* specified path.
*/
#include <nana/filesystem/file_iterator.hpp>
namespace nana
{
namespace filesystem
{
//struct fileinfo
fileinfo::fileinfo()
:size(0), directory(false)
{}
#if defined(NANA_WINDOWS)
fileinfo::fileinfo(const WIN32_FIND_DATA& wfd)
: name(wfd.cFileName), size(wfd.nFileSizeLow),
directory((FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY)
{
}
#elif defined(NANA_LINUX)
fileinfo::fileinfo(const nana::string& name, const struct stat& fst)
:name(name), size(fst.st_size), directory(0 != S_ISDIR(fst.st_mode))
{
}
#endif
//end struct fileinfo
}//end namespace filesystem
}//end namespace nana

View File

@@ -0,0 +1,443 @@
/*
* A FileSystem Utility Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/filesystem/fs_utility.cpp
* @description:
* provide some interface for file managment
*/
#include <nana/filesystem/fs_utility.hpp>
#include <nana/filesystem/file_iterator.hpp>
#include <vector>
#if defined(NANA_WINDOWS)
#include <windows.h>
#if defined(NANA_MINGW)
#ifndef _WIN32_IE
#define _WIN32_IE 0x0500
#endif
#endif
#include <shlobj.h>
#include <nana/datetime.hpp>
#elif defined(NANA_LINUX)
#include <nana/charset.hpp>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <cstdio>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#endif
namespace nana
{
namespace filesystem
{
//Because of No wide character version of POSIX
#if defined(NANA_LINUX)
typedef std::string string_t;
const char* splstr = "/\\";
#else
typedef nana::string string_t;
const nana::char_t* splstr = STR("/\\");
#endif
//class path
path::path(){}
path::path(const nana::string& text)
#if defined(NANA_WINDOWS)
:text_(text)
{
#else
:text_(nana::charset(text))
{
#endif
auto pos = text_.find_last_of(splstr);
for(; (pos != string_t::npos) && (pos + 1 == text_.size()); pos = text_.find_last_of(splstr))
text_.erase(pos);
}
bool path::empty() const
{
#if defined(NANA_WINDOWS)
return (::GetFileAttributes(text_.c_str()) == INVALID_FILE_ATTRIBUTES);
#elif defined(NANA_LINUX)
struct stat sta;
return (::stat(text_.c_str(), &sta) == -1);
#endif
}
path path::root() const
{
#if defined(NANA_WINDOWS)
return path(filesystem::root(text_));
#elif defined(NANA_LINUX)
return path(filesystem::root(nana::charset(text_)));
#endif
}
int path::what() const
{
#if defined(NANA_WINDOWS)
unsigned long attr = ::GetFileAttributes(text_.c_str());
if(INVALID_FILE_ATTRIBUTES == attr)
return type::not_exist;
if(FILE_ATTRIBUTE_DIRECTORY & attr)
return type::directory;
return type::file;
#elif defined(NANA_LINUX)
struct stat sta;
if(-1 == ::stat(text_.c_str(), &sta))
return type::not_exist;
if((S_IFDIR & sta.st_mode) == S_IFDIR)
return type::directory;
if((S_IFREG & sta.st_mode) == S_IFREG)
return type::file;
return type::not_exist;
#endif
}
nana::string path::name() const
{
string_t::size_type pos = text_.find_last_of(splstr);
#if defined(NANA_WINDOWS)
return text_.substr(pos + 1);
#else
return nana::charset(text_.substr(pos + 1));
#endif
}
//end class path
namespace detail
{
//rm_dir_recursive
//@brief: remove a directory, if it is not empty, recursively remove it's subfiles and sub directories
bool rm_dir_recursive(nana::string&& dir)
{
std::vector<file_iterator::value_type> files;
nana::string path = dir;
path += '\\';
std::copy(file_iterator(dir), file_iterator(), std::back_inserter(files));
for(auto & f : files)
{
if(f.directory)
rm_dir_recursive(path + f.name);
else
rmfile((path + f.name).c_str());
}
return rmdir(dir.c_str(), true);
}
bool mkdir_helper(const nana::string& dir, bool & if_exist)
{
#if defined(NANA_WINDOWS)
if(::CreateDirectory(dir.c_str(), 0))
{
if_exist = false;
return true;
}
if_exist = (::GetLastError() == ERROR_ALREADY_EXISTS);
#elif defined(NANA_LINUX)
if(0 == ::mkdir(static_cast<std::string>(nana::charset(dir)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
{
if_exist = false;
return true;
}
if_exist = (errno == EEXIST);
#endif
return false;
}
#if defined(NANA_WINDOWS)
void filetime_to_c_tm(FILETIME& ft, struct tm& t)
{
FILETIME local_file_time;
if(::FileTimeToLocalFileTime(&ft, &local_file_time))
{
SYSTEMTIME st;
::FileTimeToSystemTime(&local_file_time, &st);
t.tm_year = st.wYear - 1900;
t.tm_mon = st.wMonth - 1;
t.tm_mday = st.wDay;
t.tm_wday = st.wDayOfWeek - 1;
t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay);
t.tm_hour = st.wHour;
t.tm_min = st.wMinute;
t.tm_sec = st.wSecond;
}
}
#endif
}//end namespace detail
bool file_attrib(const nana::string& file, attribute& attr)
{
#if defined(NANA_WINDOWS)
WIN32_FILE_ATTRIBUTE_DATA fad;
if(::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &fad))
{
LARGE_INTEGER li;
li.u.LowPart = fad.nFileSizeLow;
li.u.HighPart = fad.nFileSizeHigh;
attr.bytes = li.QuadPart;
attr.is_directory = (0 != (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
detail::filetime_to_c_tm(fad.ftLastWriteTime, attr.modified);
return true;
}
#elif defined(NANA_LINUX)
struct stat fst;
if(0 == ::stat(static_cast<std::string>(nana::charset(file)).c_str(), &fst))
{
attr.bytes = fst.st_size;
attr.is_directory = (0 != (040000 & fst.st_mode));
attr.modified = *(::localtime(&fst.st_ctime));
return true;
}
#endif
return false;
}
long long filesize(const nana::string& file)
{
#if defined(NANA_WINDOWS)
//Some compilation environment may fail to link to GetFileSizeEx
typedef BOOL (__stdcall *GetFileSizeEx_fptr_t)(HANDLE, PLARGE_INTEGER);
GetFileSizeEx_fptr_t get_file_size_ex = reinterpret_cast<GetFileSizeEx_fptr_t>(::GetProcAddress(::GetModuleHandleA("Kernel32.DLL"), "GetFileSizeEx"));
if(get_file_size_ex)
{
HANDLE handle = ::CreateFile(file.c_str(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(INVALID_HANDLE_VALUE != handle)
{
LARGE_INTEGER li;
if(!get_file_size_ex(handle, &li))
li.QuadPart = 0;
::CloseHandle(handle);
return li.QuadPart;
}
}
return 0;
#elif defined(NANA_LINUX)
FILE * stream = ::fopen(static_cast<std::string>(nana::charset(file)).c_str(), "rb");
long long size = 0;
if(stream)
{
fseeko64(stream, 0, SEEK_END);
size = ftello64(stream);
fclose(stream);
}
return size;
#endif
}
bool modified_file_time(const nana::string& file, struct tm& t)
{
#if defined(NANA_WINDOWS)
WIN32_FILE_ATTRIBUTE_DATA attr;
if(::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &attr))
{
FILETIME local_file_time;
if(::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &local_file_time))
{
SYSTEMTIME st;
::FileTimeToSystemTime(&local_file_time, &st);
t.tm_year = st.wYear - 1900;
t.tm_mon = st.wMonth - 1;
t.tm_mday = st.wDay;
t.tm_wday = st.wDayOfWeek - 1;
t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay);
t.tm_hour = st.wHour;
t.tm_min = st.wMinute;
t.tm_sec = st.wSecond;
return true;
}
}
#elif defined(NANA_LINUX)
struct stat attr;
if(0 == ::stat(static_cast<std::string>(nana::charset(file)).c_str(), &attr))
{
t = *(::localtime(&attr.st_ctime));
return true;
}
#endif
return false;
}
bool mkdir(const nana::string& path, bool & if_exist)
{
if_exist = false;
if(path.size() == 0) return false;
nana::string root;
#if defined(NANA_WINDOWS)
if(path.size() > 3 && path[1] == STR(':'))
root = path.substr(0, 3);
#elif defined(NANA_LINUX)
if(path[0] == STR('/'))
root = '/';
#endif
bool mkstat = false;
std::size_t beg = root.size();
while(true)
{
beg = path.find_first_not_of(STR("/\\"), beg);
if(beg == path.npos)
break;
std::size_t pos = path.find_first_of(STR("/\\"), beg + 1);
if(pos != path.npos)
{
root += path.substr(beg, pos - beg);
mkstat = detail::mkdir_helper(root, if_exist);
if(mkstat == false && if_exist == false)
return false;
#if defined(NANA_WINDOWS)
root += STR('\\');
#elif defined(NANA_LINUX)
root += STR('/');
#endif
}
else
{
if(beg + 1 < path.size())
{
root += path.substr(beg);
mkstat = detail::mkdir_helper(root, if_exist);
}
break;
}
beg = pos + 1;
}
return mkstat;
}
bool rmfile(const nana::char_t* file)
{
#if defined(NANA_WINDOWS)
bool ret = false;
if(file)
{
ret = (::DeleteFile(file) == TRUE);
if(!ret)
ret = (ERROR_FILE_NOT_FOUND == ::GetLastError());
}
return ret;
#elif defined(NANA_LINUX)
if(std::remove(static_cast<std::string>(nana::charset(file)).c_str()))
return (errno == ENOENT);
return true;
#endif
}
bool rmdir(const nana::char_t* dir, bool fails_if_not_empty)
{
bool ret = false;
if(dir)
{
#if defined(NANA_WINDOWS)
ret = (::RemoveDirectory(dir) == TRUE);
if(!fails_if_not_empty && (::GetLastError() == ERROR_DIR_NOT_EMPTY))
ret = detail::rm_dir_recursive(dir);
#elif defined(NANA_LINUX)
std::string mbstr = nana::charset(dir);
if(::rmdir(mbstr.c_str()))
{
if(!fails_if_not_empty && (errno == EEXIST || errno == ENOTEMPTY))
ret = detail::rm_dir_recursive(dir);
}
else
ret = true;
#endif
}
return ret;
}
nana::string root(const nana::string& path)
{
std::size_t index = path.size();
if(index)
{
const nana::char_t * str = path.c_str();
for(--index; index > 0; --index)
{
nana::char_t c = str[index];
if(c != '\\' && c != '/')
break;
}
for(--index; index > 0; --index)
{
nana::char_t c = str[index];
if(c == '\\' || c == '/')
break;
}
}
return index?path.substr(0, index + 1):nana::string();
}
nana::string path_user()
{
#if defined(NANA_WINDOWS)
nana::char_t path[MAX_PATH];
if(SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, path)))
return path;
#elif defined(NANA_LINUX)
const char * s = ::getenv("HOME");
if(s)
return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8);
#endif
return nana::string();
}
nana::string path_current()
{
#if defined(NANA_WINDOWS)
nana::char_t buf[MAX_PATH];
DWORD len = ::GetCurrentDirectory(MAX_PATH, buf);
if(len)
{
if(len > MAX_PATH)
{
nana::char_t * p = new nana::char_t[len + 1];
::GetCurrentDirectory(len + 1, p);
nana::string s = p;
delete [] p;
return s;
}
return buf;
}
#elif defined(NANA_LINUX)
const char * s = ::getenv("PWD");
if(s)
return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8);
#endif
return nana::string();
}
}//end namespace filesystem
}//end namespace nana

642
source/gui/animation.cpp Normal file
View File

@@ -0,0 +1,642 @@
/*
* An Animation Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/animation.cpp
*/
#include <nana/gui/animation.hpp>
#include <nana/gui/drawing.hpp>
#include <nana/system/timepiece.hpp>
#include <nana/system/platform.hpp>
#include <vector>
#include <list>
#include <algorithm>
#if defined(NANA_MINGW) && defined(STD_THREAD_NOT_SUPPORTED)
#include <nana/std_thread.hpp>
#include <nana/std_mutex.hpp>
#include <nana/std_condition_variable.hpp>
#else
#include <mutex>
#include <condition_variable>
#include <thread>
#endif //NANA_MINGW
namespace nana
{
class animation;
struct output_t
{
drawing::diehard_t diehard;
std::vector<nana::point> points;
output_t()
: diehard(nullptr)
{}
};
struct framebuilder
{
std::size_t length;
std::function<bool(std::size_t, paint::graphics&, nana::size&)> frbuilder;
framebuilder(const std::function<bool(std::size_t, paint::graphics&, nana::size&)>& f, std::size_t l)
: length(l), frbuilder(f)
{}
framebuilder(std::size_t l, std::function<bool(std::size_t, paint::graphics&, nana::size)>&& f)
: length(l), frbuilder(std::move(f))
{}
};
struct frame
{
enum class kind
{
oneshot,
framebuilder
};
frame(const paint::image& r)
: type(kind::oneshot)
{
u.oneshot = new paint::image(r);
}
frame(paint::image&& r)
: type(kind::oneshot)
{
u.oneshot = new paint::image(std::move(r));
}
frame(const std::function<bool(std::size_t, paint::graphics&, nana::size&)>& frbuilder, std::size_t length)
: type(kind::framebuilder)
{
u.frbuilder = new framebuilder(frbuilder, length);
}
frame(std::function<bool(std::size_t, paint::graphics&, nana::size&)>&& frbuilder, std::size_t length)
: type(kind::framebuilder)
{
u.frbuilder = new framebuilder(frbuilder, length);
}
frame(const frame& r)
: type(r.type)
{
switch(type)
{
case kind::oneshot:
u.oneshot = new paint::image(*r.u.oneshot);
break;
case kind::framebuilder:
u.frbuilder = new framebuilder(*r.u.frbuilder);
break;
}
}
frame(frame&& r)
: type(r.type)
{
u = r.u;
r.u.oneshot = nullptr;
}
~frame()
{
switch(type)
{
case kind::oneshot:
delete u.oneshot;
break;
case kind::framebuilder:
delete u.frbuilder;
break;
}
}
frame& operator=(const frame& r)
{
if(this != &r)
{
switch(type)
{
case kind::oneshot:
delete u.oneshot;
break;
case kind::framebuilder:
delete u.frbuilder;
break;
}
type = r.type;
switch(type)
{
case kind::oneshot:
u.oneshot = new paint::image(*r.u.oneshot);
break;
case kind::framebuilder:
u.frbuilder = new framebuilder(*r.u.frbuilder);
break;
}
}
return *this;
}
frame& operator=(frame&& r)
{
if(this != &r)
{
switch(type)
{
case kind::oneshot:
delete u.oneshot;
break;
case kind::framebuilder:
delete u.frbuilder;
break;
}
type = r.type;
u = r.u;
r.u.oneshot = nullptr;
}
return *this;
}
std::size_t length() const
{
switch(type)
{
case kind::oneshot:
return 1;
case kind::framebuilder:
return u.frbuilder->length;
}
return 0;
}
//
kind type;
union uframes
{
paint::image * oneshot;
framebuilder * frbuilder;
}u;
};
//class frameset
//struct frameset::impl
struct frameset::impl
{
//Only list whos iterator would not invalided after a insertion.
std::list<frame> frames;
std::list<frame>::iterator this_frame;
std::size_t pos_in_this_frame;
mutable bool good_frame_by_frmbuilder; //It indicates the state of frame whether is valid.
impl()
: this_frame(frames.end()), pos_in_this_frame(0),
good_frame_by_frmbuilder(false)
{}
//Render A frame on the set of windows.
void render_this(std::map<window, output_t>& outs, paint::graphics& framegraph, nana::size& framegraph_dimension) const
{
if(this_frame == frames.end())
return;
frame & frmobj = *this_frame;
switch(frmobj.type)
{
case frame::kind::oneshot:
_m_render(outs, [&frmobj](paint::graphics& tar, const nana::point& pos)
{
frmobj.u.oneshot->paste(tar, pos.x, pos.y);
});
break;
case frame::kind::framebuilder:
good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension);
if(good_frame_by_frmbuilder)
{
nana::rectangle r = framegraph_dimension;
_m_render(outs, [&r, &framegraph](paint::graphics& tar, const nana::point& pos) mutable
{
r.x = pos.x;
r.y = pos.y;
tar.bitblt(r, framegraph);
});
}
break;
}
}
//Render a frame on a specified window graph
void render_this(paint::graphics& graph, const nana::point& pos, paint::graphics& framegraph, nana::size& framegraph_dimension, bool rebuild_frame) const
{
if(this_frame == frames.end())
return;
frame & frmobj = *this_frame;
switch(frmobj.type)
{
case frame::kind::oneshot:
frmobj.u.oneshot->paste(graph, pos.x, pos.y);
break;
case frame::kind::framebuilder:
if(rebuild_frame)
good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension);
if(good_frame_by_frmbuilder)
{
nana::rectangle r(pos, framegraph_dimension);
graph.bitblt(r, framegraph);
}
break;
}
}
bool eof() const
{
return (frames.end() == this_frame);
}
void next_frame()
{
if(frames.end() == this_frame)
return;
frame & frmobj = *this_frame;
switch(frmobj.type)
{
case frame::kind::oneshot:
++this_frame;
pos_in_this_frame = 0;
break;
case frame::kind::framebuilder:
if(pos_in_this_frame >= frmobj.u.frbuilder->length)
{
pos_in_this_frame = 0;
++this_frame;
}
else
++pos_in_this_frame;
break;
default:
throw std::runtime_error("Nana.GUI.Animation: Bad frame type");
}
}
//Seek to the first frame
void reset()
{
this_frame = frames.begin();
pos_in_this_frame = 0;
}
private:
template<typename Renderer>
void _m_render(std::map<window, output_t>& outs, Renderer renderer) const
{
for(auto & tar: outs)
{
auto graph = API::dev::window_graphics(tar.first);
if(nullptr == graph)
continue;
for(auto & outp : tar.second.points)
renderer(*graph, outp);
API::update_window(tar.first);
}
}
};//end struct frameset::impl
//public:
frameset::frameset()
: impl_(new impl)
{}
void frameset::push_back(const paint::image& m)
{
bool located = impl_->this_frame != impl_->frames.end();
impl_->frames.emplace_back(m);
if(false == located)
impl_->this_frame = impl_->frames.begin();
}
void frameset::push_back(paint::image&& m)
{
impl_->frames.emplace_back(std::move(m));
if(1 == impl_->frames.size())
impl_->this_frame = impl_->frames.begin();
}
void frameset::push_back(framebuilder&fb, std::size_t length)
{
impl_->frames.emplace_back(fb, length);
if(1 == impl_->frames.size())
impl_->this_frame = impl_->frames.begin();
}
void frameset::push_back(framebuilder&& fb, std::size_t length)
{
impl_->frames.emplace_back(std::move(fb), length);
if(1 == impl_->frames.size())
impl_->this_frame = impl_->frames.begin();
}
//end class frameset
//class animation
class animation::performance_manager
{
public:
struct thread_variable
{
std::mutex mutex;
std::condition_variable condvar;
std::vector<impl*> animations;
std::size_t active; //The number of active animations
std::shared_ptr<std::thread> thread;
double performance_parameter;
};
thread_variable * insert(impl* p);
void close(impl* p);
bool empty() const;
private:
void _m_perf_thread(thread_variable* thrvar);
private:
mutable std::recursive_mutex mutex_;
std::vector<thread_variable*> threads_;
}; //end class animation::performance_manager
struct animation::impl
{
bool looped;
volatile bool paused;
std::list<frameset> framesets;
std::map<std::string, branch_t> branches;
std::map<window, output_t> outputs;
paint::graphics framegraph; //framegraph will be created by framebuilder
nana::size framegraph_dimension;
struct state_t
{
std::list<frameset>::iterator this_frameset;
}state;
performance_manager::thread_variable * thr_variable;
static performance_manager * perf_manager;
impl()
: looped(false), paused(true)
{
state.this_frameset = framesets.begin();
{
nana::internal_scope_guard lock;
if(nullptr == perf_manager)
perf_manager = new performance_manager;
}
thr_variable = perf_manager->insert(this);
}
~impl()
{
perf_manager->close(this);
{
nana::internal_scope_guard lock;
if(perf_manager->empty())
{
delete perf_manager;
perf_manager = nullptr;
}
}
}
void render_this_specifically(paint::graphics& graph, const nana::point& pos)
{
if(state.this_frameset != framesets.end())
state.this_frameset->impl_->render_this(graph, pos, framegraph, framegraph_dimension, false);
}
void render_this_frame()
{
if(state.this_frameset != framesets.end())
state.this_frameset->impl_->render_this(outputs, framegraph, framegraph_dimension);
}
bool move_to_next()
{
if(state.this_frameset != framesets.end())
{
state.this_frameset->impl_->next_frame();
return (!state.this_frameset->impl_->eof());
}
return false;
}
//Seek to the first frameset
void reset()
{
state.this_frameset = framesets.begin();
if(state.this_frameset != framesets.end())
state.this_frameset->impl_->reset();
}
};//end struct animation::impl
//class animation::performance_manager
auto animation::performance_manager::insert(impl* p) -> thread_variable *
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
for(auto thr : threads_)
{
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
if(thr->performance_parameter / (thr->animations.size() + 1) <= 43.3)
{
thr->animations.push_back(p);
return thr;
}
}
auto thr = new thread_variable;
thr->animations.push_back(p);
thr->performance_parameter = 0.0;
thr->thread = std::make_shared<std::thread>([this, thr]()
{
_m_perf_thread(thr);
});
threads_.push_back(thr);
return thr;
}
void animation::performance_manager::close(impl* p)
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
for(auto thr : threads_)
{
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
auto i = std::find(thr->animations.begin(), thr->animations.end(), p);
if(i != thr->animations.end())
{
thr->animations.erase(i);
return;
}
}
}
bool animation::performance_manager::empty() const
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
for(auto thr : threads_)
{
if(thr->animations.size())
return false;
}
return true;
}
void animation::performance_manager::_m_perf_thread(thread_variable* thrvar)
{
nana::system::timepiece tmpiece;
while(true)
{
thrvar->active = 0;
tmpiece.start();
{
std::lock_guard<decltype(thrvar->mutex)> lock(thrvar->mutex);
for(auto ani : thrvar->animations)
{
if(ani->paused)
continue;
ani->render_this_frame();
if(false == ani->move_to_next())
{
if(ani->looped)
{
ani->reset();
++thrvar->active;
}
}
else
++thrvar->active;
}
}
if(thrvar->active)
{
thrvar->performance_parameter = tmpiece.calc();
if(thrvar->performance_parameter < 43.4)
nana::system::sleep(static_cast<unsigned>(43.4 - thrvar->performance_parameter));
}
else
{
//There isn't an active frame, then let the thread
//wait for a signal for an active animation
std::unique_lock<std::mutex> lock(thrvar->mutex);
if(0 == thrvar->active)
thrvar->condvar.wait(lock);
}
}
}
//end class animation::performance_manager
animation::animation()
: impl_(new impl)
{
}
void animation::push_back(const frameset& frms)
{
impl_->framesets.emplace_back(frms);
if(1 == impl_->framesets.size())
impl_->state.this_frameset = impl_->framesets.begin();
}
/*
void branch(const std::string& name, const frameset& frms)
{
impl_->branches[name].frames = frms;
}
void branch(const std::string& name, const frameset& frms, std::function<std::size_t(const std::string&, std::size_t, std::size_t&)> condition)
{
auto & br = impl_->branches[name];
br.frames = frms;
br.condition = condition;
}
*/
void animation::looped(bool enable)
{
if(impl_->looped != enable)
{
impl_->looped = enable;
if(enable)
{
std::unique_lock<std::mutex> lock(impl_->thr_variable->mutex);
if(0 == impl_->thr_variable->active)
{
impl_->thr_variable->active = 1;
impl_->thr_variable->condvar.notify_one();
}
}
}
}
void animation::play()
{
impl_->paused = false;
std::unique_lock<std::mutex> lock(impl_->thr_variable->mutex);
if(0 == impl_->thr_variable->active)
{
impl_->thr_variable->active = 1;
impl_->thr_variable->condvar.notify_one();
}
}
void animation::pause()
{
impl_->paused = true;
}
void animation::output(window wd, const nana::point& pos)
{
auto & output = impl_->outputs[wd];
if(nullptr == output.diehard)
{
drawing dw(wd);
output.diehard = dw.draw_diehard([this, pos](paint::graphics& tar){
impl_->render_this_specifically(tar, pos);
});
API::events(wd).destroy.connect([this](const arg_destroy& arg){
std::lock_guard<decltype(impl_->thr_variable->mutex)> lock(impl_->thr_variable->mutex);
impl_->outputs.erase(arg.window_handle);
});
}
output.points.push_back(pos);
}
//end class animation
animation::performance_manager * animation::impl::perf_manager;
} //end namespace nana

33
source/gui/basis.cpp Normal file
View File

@@ -0,0 +1,33 @@
/*
* Basis Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/basis.cpp
*
* This file provides basis class and data structrue that required by gui
*/
#include <nana/gui/basis.hpp>
namespace nana
{
//struct appearance
//@brief: Window appearance structure
appearance::appearance()
:taskbar(true), floating(false), no_activate(false),
minimize(true), maximize(true), sizable(true),
decoration(true)
{}
appearance::appearance(bool has_decorate, bool taskbar, bool is_float, bool no_activate, bool min, bool max, bool sizable)
: taskbar(taskbar), floating(is_float), no_activate(no_activate),
minimize(min), maximize(max), sizable(sizable),
decoration(has_decorate)
{}
//end struct appearance
}//end namespace nana

View File

@@ -0,0 +1,385 @@
#include <nana/gui/detail/basic_window.hpp>
#include <nana/gui/detail/native_window_interface.hpp>
namespace nana
{
namespace detail
{
//class caret_descriptor
caret_descriptor::caret_descriptor(core_window_t* wd, unsigned width, unsigned height)
:wd_(wd), size_(width, height), visible_(false), real_visible_state_(false), out_of_range_(false)
{}
caret_descriptor::~caret_descriptor()
{
if(wd_) native_interface::caret_destroy(wd_->root);
}
void caret_descriptor::set_active(bool active)
{
if(wd_)
{
if(active)
{
native_interface::caret_create(wd_->root, size_.width, size_.height);
real_visible_state_ = false;
visible_ = false;
this->position(point_.x, point_.y);
}
else
native_interface::caret_destroy(wd_->root);
wd_->root_widget->other.attribute.root->ime_enabled = active;
}
}
auto caret_descriptor::window() const ->core_window_t*
{
return wd_;
}
void caret_descriptor::position(int x, int y)
{
point_.x = x;
point_.y = y;
update();
}
void caret_descriptor::effective_range(nana::rectangle rect)
{
//Chech rect
if( (rect.width && rect.height) &&
(rect.x + rect.width > 0) &&
(rect.y + rect.height > 0))
{
if(rect.x < 0)
{
rect.width += rect.x;
rect.x = 0;
}
if(rect.y < 0)
{
rect.height += rect.y;
rect.y = 0;
}
if(effective_range_ != rect)
{
effective_range_ = rect;
update();
}
}
}
nana::point caret_descriptor::position() const
{
return point_;
}
void caret_descriptor::visible(bool isshow)
{
if(visible_ != isshow)
{
visible_ = isshow;
if(visible_ == false || false == out_of_range_)
_m_visible(isshow);
}
}
bool caret_descriptor::visible() const
{
return visible_;
}
nana::size caret_descriptor::size() const
{
return size_;
}
void caret_descriptor::size(const nana::size& s)
{
size_ = s;
update();
if(visible_) this->visible(true);
}
void caret_descriptor::_m_visible(bool isshow)
{
if(real_visible_state_ != isshow)
{
real_visible_state_ = isshow;
native_interface::caret_visible(wd_->root, isshow);
}
}
void caret_descriptor::update()
{
nana::point pos = point_;
nana::size size = size_;
nana::rectangle rect = effective_range_;
if(0 == effective_range_.width || 0 == effective_range_.height)
{
rect.x = rect.y = 0;
rect = wd_->dimension;
}
else
{
pos.x += effective_range_.x;
pos.y += effective_range_.y;
}
if( (pos.x + static_cast<int>(size.width) <= rect.x) || (pos.x >= rect.x + static_cast<int>(rect.width)) ||
(pos.y + static_cast<int>(size.height) <= rect.y) || (pos.y >= rect.y + static_cast<int>(rect.height))
)
{//Out of Range without overlap
if(false == out_of_range_)
{
out_of_range_ = true;
if(visible_)
_m_visible(false);
}
}
else
{
if(pos.x < rect.x)
{
size.width -= (rect.x - pos.x);
pos.x = rect.x;
}
else if(pos.x + size.width > rect.x + rect.width)
{
size.width -= pos.x + size.width - (rect.x + rect.width);
}
if(pos.y < rect.y)
{
size.width -= (rect.y - pos.y);
pos.y = rect.y;
}
else if(pos.y + size.height > rect.y + rect.height)
size.height -= pos.y + size.height - (rect.y + rect.height);
if(out_of_range_)
{
if(paint_size_ == size)
_m_visible(true);
out_of_range_ = false;
}
if(paint_size_ != size)
{
native_interface::caret_destroy(wd_->root);
native_interface::caret_create(wd_->root, size.width, size.height);
real_visible_state_ = false;
if(visible_)
_m_visible(true);
paint_size_ = size;
}
native_interface::caret_pos(wd_->root, wd_->pos_root.x + pos.x, wd_->pos_root.y + pos.y);
}
}
//end class caret_descriptor
//struct basic_window
//struct basic_window::other_tag
basic_window::other_tag::other_tag(category::flags categ)
: category(categ), active_window(nullptr), upd_state(update_state::none)
{
switch(categ)
{
case category::root_tag::value:
attribute.root = new attr_root_tag;
attribute.root->context.focus_changed = false;
break;
case category::frame_tag::value:
attribute.frame = new attr_frame_tag;
break;
default:
attribute.root = nullptr;
}
}
basic_window::other_tag::~other_tag()
{
switch(category)
{
case category::root_tag::value:
delete attribute.root;
break;
case category::frame_tag::value:
delete attribute.frame;
break;
default: break;
}
}
//end struct basic_window::other_tag
//basic_window
//@brief: constructor for the root window
basic_window::basic_window(basic_window* owner, widget* wdg, category::root_tag**)
: widget_ptr(wdg), other(category::root_tag::value)
{
drawer.bind(this);
_m_init_pos_and_size(0, rectangle());
this->_m_initialize(owner);
}
basic_window::~basic_window()
{
delete together.caret;
together.caret = nullptr;
delete effect.bground;
effect.bground = nullptr;
}
//bind_native_window
//@brief: bind a native window and baisc_window
void basic_window::bind_native_window(native_window_type wd, unsigned width, unsigned height, unsigned extra_width, unsigned extra_height, nana::paint::graphics& graphics)
{
if(category::root_tag::value == this->other.category)
{
this->root = wd;
dimension.width = width;
dimension.height = height;
this->extra_width = extra_width;
this->extra_height = extra_height;
this->root_widget = this;
this->root_graph = &graphics;
}
}
void basic_window::frame_window(native_window_type wd)
{
if(category::frame_tag::value == this->other.category)
other.attribute.frame->container = wd;
}
bool basic_window::is_ancestor_of(const basic_window* wd) const
{
while (wd)
{
if (this == wd->parent)
return true;
wd = wd->parent;
}
return false;
}
bool basic_window::visible_parents() const
{
for (auto pnt = parent; pnt; pnt = pnt->parent)
{
if (!pnt->visible)
return false;
}
return true;
}
bool basic_window::belong_to_lazy() const
{
for (auto wd = this; wd; wd = wd->parent)
{
if (basic_window::update_state::refresh == wd->other.upd_state)
return true;
}
return false;
}
void basic_window::_m_init_pos_and_size(basic_window* parent, const rectangle& r)
{
pos_owner = pos_root = r;
dimension = r;
if(parent)
{
pos_root.x += parent->pos_root.x;
pos_root.y += parent->pos_root.y;
}
}
void basic_window::_m_initialize(basic_window* agrparent)
{
if(other.category == category::root_tag::value)
{
if(agrparent && (nana::system::this_thread_id() != agrparent->thread_id))
agrparent = nullptr;
while(agrparent && (agrparent->other.category != category::root_tag::value))
agrparent = agrparent->parent;
owner = agrparent;
parent = nullptr;
index = 0;
}
else
{
parent = agrparent;
owner = nullptr;
root_widget = agrparent->root_widget;
root = agrparent->root;
root_graph = agrparent->root_graph;
index = static_cast<unsigned>(agrparent->children.size());
agrparent->children.push_back(this);
}
predef_cursor = cursor::arrow;
flags.capture = false;
flags.dbl_click = true;
flags.enabled = true;
flags.modal = false;
flags.take_active = true;
flags.dropable = false;
flags.fullscreen = false;
flags.tab = nana::detail::tab_type::none;
flags.action = mouse_action::normal;
flags.refreshing = false;
flags.destroying = false;
flags.borderless = false;
visible = false;
color.foreground = 0x0;
color.background = nana::color::button_face;
color.active = 0x60C8FD;
effect.edge_nimbus = effects::edge_nimbus::none;
effect.bground = nullptr;
effect.bground_fade_rate = 0;
together.caret = nullptr;
together.attached_events = nullptr;
extra_width = extra_height = 0;
//The window must keep its thread_id same as its parent if it is a child.
//Otherwise, its root buffer would be mapped repeatly if it is in its parent thread.
thread_id = nana::system::this_thread_id();
if(agrparent && (thread_id != agrparent->thread_id))
thread_id = agrparent->thread_id;
}
bool basic_window::set_events(const std::shared_ptr<general_events>& p)
{
if (together.attached_events)
return false;
together.events_ptr = p;
together.attached_events = p.get();
return true;
}
general_events * basic_window::get_events() const
{
return together.attached_events;
}
//end struct basic_window
}//end namespace detail
}//end namespace nana

View File

@@ -0,0 +1,331 @@
/*
* A Bedrock Platform-Independent Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/detail/bedrock_pi.cpp
*/
#include <nana/config.hpp>
#include PLATFORM_SPEC_HPP
#include GUI_BEDROCK_HPP
#include <nana/gui/detail/event_code.hpp>
#include <nana/system/platform.hpp>
#include <sstream>
#include <nana/system/timepiece.hpp>
#include <nana/gui/wvl.hpp>
#include <nana/gui/detail/inner_fwd_implement.hpp>
#include <nana/gui/detail/native_window_interface.hpp>
#include <nana/gui/layout_utility.hpp>
#include <nana/gui/detail/element_store.hpp>
namespace nana
{
namespace detail
{
void events_operation_register(event_handle evt)
{
bedrock::instance().evt_operation.register_evt(evt);
}
void events_operation_cancel(event_handle evt)
{
bedrock::instance().evt_operation.cancel(evt);
}
void bedrock::event_expose(core_window_t * wd, bool exposed)
{
if (nullptr == wd) return;
wd->visible = exposed;
arg_expose arg;
arg.exposed = exposed;
arg.window_handle = reinterpret_cast<window>(wd);
if (emit(event_code::expose, wd, arg, false, get_thread_context()))
{
if (!exposed)
{
if (category::flags::root != wd->other.category)
{
//If the wd->parent is a lite_widget then find a parent until it is not a lite_widget
wd = wd->parent;
while (category::flags::lite_widget == wd->other.category)
wd = wd->parent;
}
else if (category::flags::frame == wd->other.category)
wd = wd_manager.find_window(wd->root, wd->pos_root.x, wd->pos_root.y);
}
wd_manager.refresh_tree(wd);
wd_manager.map(wd);
}
}
void bedrock::event_move(core_window_t* wd, int x, int y)
{
if (wd)
{
arg_move arg;
arg.window_handle = reinterpret_cast<window>(wd);
arg.x = x;
arg.y = y;
if (emit(event_code::move, wd, arg, false, get_thread_context()))
wd_manager.update(wd, true, true);
}
}
bool bedrock::event_msleave(core_window_t* hovered)
{
if (wd_manager.available(hovered) && hovered->flags.enabled)
{
hovered->flags.action = mouse_action::normal;
arg_mouse arg;
arg.evt_code = event_code::mouse_leave;
arg.window_handle = reinterpret_cast<window>(hovered);
arg.pos.x = arg.pos.y = 0;
arg.left_button = arg.right_button = arg.mid_button = false;
arg.ctrl = arg.shift = false;
emit(event_code::mouse_leave, hovered, arg, true, get_thread_context());
return true;
}
return false;
}
void bedrock::update_cursor(core_window_t * wd)
{
internal_scope_guard isg;
if (wd_manager.available(wd))
{
auto * thrd = get_thread_context(wd->thread_id);
if (nullptr == thrd)
return;
auto pos = native_interface::cursor_position();
auto native_handle = native_interface::find_window(pos.x, pos.y);
if (!native_handle)
return;
native_interface::calc_window_point(native_handle, pos);
if (wd != wd_manager.find_window(native_handle, pos.x, pos.y))
return;
set_cursor(wd, wd->predef_cursor, thrd);
}
}
void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::detail::event_arg_interface& event_arg)
{
switch (evt_code)
{
case event_code::click:
case event_code::dbl_click:
case event_code::mouse_enter:
case event_code::mouse_move:
case event_code::mouse_leave:
case event_code::mouse_down:
case event_code::mouse_up:
{
auto arg = dynamic_cast<const arg_mouse*>(&event_arg);
if (nullptr == arg)
return;
void(::nana::detail::drawer::*drawer_event_fn)(const arg_mouse&);
::nana::basic_event<arg_mouse>* evt_addr;
switch (evt_code)
{
case event_code::click:
drawer_event_fn = &drawer::click;
evt_addr = &wd->together.attached_events->click;
break;
case event_code::dbl_click:
drawer_event_fn = &drawer::dbl_click;
evt_addr = &wd->together.attached_events->dbl_click;
break;
case event_code::mouse_enter:
drawer_event_fn = &drawer::mouse_enter;
evt_addr = &wd->together.attached_events->mouse_enter;
break;
case event_code::mouse_move:
drawer_event_fn = &drawer::mouse_move;
evt_addr = &wd->together.attached_events->mouse_move;
break;
case event_code::mouse_leave:
drawer_event_fn = &drawer::mouse_leave;
evt_addr = &wd->together.attached_events->mouse_leave;
break;
case event_code::mouse_down:
drawer_event_fn = &drawer::mouse_down;
evt_addr = &wd->together.attached_events->mouse_down;
break;
case event_code::mouse_up:
drawer_event_fn = &drawer::mouse_up;
evt_addr = &wd->together.attached_events->mouse_up;
break;
default:
throw std::runtime_error("Invalid mouse event code");
}
(wd->drawer.*drawer_event_fn)(*arg);
if (!draw_only)
evt_addr->emit(*arg);
break;
}
case event_code::mouse_wheel:
{
auto arg = dynamic_cast<const arg_wheel*>(&event_arg);
if (arg)
{
wd->drawer.mouse_wheel(*arg);
if (!draw_only)
wd->together.attached_events->mouse_wheel.emit(*arg);
}
break;
}
case event_code::key_press:
case event_code::key_char:
case event_code::key_release:
case event_code::shortkey:
{
auto arg = dynamic_cast<const arg_keyboard*>(&event_arg);
if (nullptr == arg)
return;
void(::nana::detail::drawer::*drawer_event_fn)(const arg_keyboard&);
::nana::basic_event<arg_keyboard>* evt_addr;
switch (evt_code)
{
case event_code::key_press:
drawer_event_fn = &drawer::key_press;
evt_addr = &wd->together.attached_events->key_press;
break;
case event_code::key_char:
drawer_event_fn = &drawer::key_char;
evt_addr = &wd->together.attached_events->key_char;
break;
case event_code::key_release:
drawer_event_fn = &drawer::key_release;
evt_addr = &wd->together.attached_events->key_release;
break;
case event_code::shortkey:
drawer_event_fn = &drawer::shortkey;
evt_addr = &wd->together.attached_events->shortkey;
break;
default:
throw std::runtime_error("Invalid keyboard event code");
}
(wd->drawer.*drawer_event_fn)(*arg);
if (!draw_only)
evt_addr->emit(*arg);
break;
}
case event_code::expose:
if (!draw_only)
{
auto arg = dynamic_cast<const arg_expose*>(&event_arg);
if (arg)
wd->together.attached_events->expose.emit(*arg);
}
break;
case event_code::focus:
{
auto arg = dynamic_cast<const arg_focus*>(&event_arg);
if (arg)
{
wd->drawer.focus(*arg);
if (!draw_only)
wd->together.attached_events->focus.emit(*arg);
}
break;
}
case event_code::move:
{
auto arg = dynamic_cast<const arg_move*>(&event_arg);
if (arg)
{
wd->drawer.move(*arg);
if (!draw_only)
wd->together.attached_events->move.emit(*arg);
}
break;
}
case event_code::resizing:
{
auto arg = dynamic_cast<const arg_resizing*>(&event_arg);
if (arg)
{
wd->drawer.resizing(*arg);
if (!draw_only)
wd->together.attached_events->resizing.emit(*arg);
}
break;
}
case event_code::resized:
{
auto arg = dynamic_cast<const arg_resized*>(&event_arg);
if (arg)
{
wd->drawer.resized(*arg);
if (!draw_only)
wd->together.attached_events->resized.emit(*arg);
}
break;
}
case event_code::unload:
if (!draw_only)
{
auto arg = dynamic_cast<const arg_unload*>(&event_arg);
if (arg && (wd->other.category == category::flags::root))
{
auto evt_ptr = dynamic_cast<events_root_extension*>(wd->together.attached_events);
if (evt_ptr)
evt_ptr->unload.emit(*arg);
}
}
break;
case event_code::destroy:
if (!draw_only)
{
auto arg = dynamic_cast<const arg_destroy*>(&event_arg);
if (arg)
wd->together.attached_events->destroy.emit(*arg);
}
break;
default:
throw std::runtime_error("Invalid event code");
}
}
void bedrock::_m_except_handler()
{
std::vector<core_window_t*> v;
wd_manager.all_handles(v);
if (v.size())
{
std::vector<native_window_type> roots;
native_window_type root = nullptr;
unsigned tid = nana::system::this_thread_id();
for (auto wd : v)
{
if ((wd->thread_id == tid) && (wd->root != root))
{
root = wd->root;
if (roots.cend() == std::find(roots.cbegin(), roots.cend(), root))
roots.push_back(root);
}
}
for (auto i : roots)
interface_type::close_window(i);
}
}
}//end namespace detail
}//end namespace nana

View File

@@ -0,0 +1,23 @@
/*
* Bedrock Selector
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Nana Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://nanapro.sourceforge.net/LICENSE_1_0.txt)
*
* @file: nana/gui/detail/bedrock_selector.cpp
*
* This file is used to support the Nana project of some cross-platform IDE,
*
*/
#include <nana/config.hpp>
#if defined(NANA_WINDOWS)
#include "win32/bedrock.cpp"
#elif defined(NANA_LINUX)
#include "linux_X11/bedrock.cpp"
#endif

View File

@@ -0,0 +1,388 @@
/*
* A Drawer Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/detail/drawer.cpp
*/
#include <nana/config.hpp>
#include GUI_BEDROCK_HPP
#include <nana/gui/detail/drawer.hpp>
#include <nana/gui/detail/dynamic_drawing_object.hpp>
#include <nana/gui/detail/effects_renderer.hpp>
#include <nana/gui/detail/basic_window.hpp>
#if defined(NANA_X11)
#include <nana/detail/linux_X11/platform_spec.hpp>
#endif
#include <algorithm>
namespace nana
{
typedef detail::edge_nimbus_renderer<detail::bedrock::core_window_t> edge_nimbus_renderer_t;
//class drawer_trigger
drawer_trigger::~drawer_trigger(){}
void drawer_trigger::attached(widget_reference, graph_reference){}
void drawer_trigger::detached(){} //none-const
void drawer_trigger::typeface_changed(graph_reference){}
void drawer_trigger::refresh(graph_reference){}
void drawer_trigger::resizing(graph_reference, const arg_resizing&)
{
overrided_ = false;
}
void drawer_trigger::resized(graph_reference graph, const arg_resized&)
{
overrided_ = true;
this->refresh(graph);
detail::bedrock::instance().thread_context_lazy_refresh();
}
void drawer_trigger::move(graph_reference, const arg_move&)
{
overrided_ = false;
}
void drawer_trigger::click(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::dbl_click(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_enter(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_move(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_leave(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_down(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_up(graph_reference, const arg_mouse&)
{
overrided_ = false;
}
void drawer_trigger::mouse_wheel(graph_reference, const arg_wheel&)
{
overrided_ = false;
}
void drawer_trigger::mouse_dropfiles(graph_reference, const arg_dropfiles&)
{
overrided_ = false;
}
void drawer_trigger::focus(graph_reference, const arg_focus&)
{
overrided_ = false;
}
void drawer_trigger::key_press(graph_reference, const arg_keyboard&)
{
overrided_ = false;
}
void drawer_trigger::key_char(graph_reference, const arg_keyboard&)
{
overrided_ = false;
}
void drawer_trigger::key_release(graph_reference, const arg_keyboard&)
{
overrided_ = false;
}
void drawer_trigger::shortkey(graph_reference, const arg_keyboard&)
{
overrided_ = false;
}
void drawer_trigger::_m_reset_overrided()
{
overrided_ = true;
}
bool drawer_trigger::_m_overrided() const
{
return overrided_;
}
//end class drawer_trigger
namespace detail
{
typedef bedrock bedrock_type;
//class drawer
drawer::~drawer()
{
for(auto p : dynamic_drawing_objects_)
{
delete p;
}
}
void drawer::bind(basic_window* cw)
{
core_window_ = cw;
}
void drawer::typeface_changed()
{
if(realizer_)
realizer_->typeface_changed(graphics);
}
void drawer::click(const arg_mouse& arg)
{
_m_emit(event_code::click, arg, &drawer_trigger::click);
}
void drawer::dbl_click(const arg_mouse& arg)
{
_m_emit(event_code::dbl_click, arg, &drawer_trigger::dbl_click);
}
void drawer::mouse_enter(const arg_mouse& arg)
{
_m_emit(event_code::mouse_enter, arg, &drawer_trigger::mouse_enter);
}
void drawer::mouse_move(const arg_mouse& arg)
{
_m_emit(event_code::mouse_move, arg, &drawer_trigger::mouse_move);
}
void drawer::mouse_leave(const arg_mouse& arg)
{
_m_emit(event_code::mouse_leave, arg, &drawer_trigger::mouse_leave);
}
void drawer::mouse_down(const arg_mouse& arg)
{
_m_emit(event_code::mouse_down, arg, &drawer_trigger::mouse_down);
}
void drawer::mouse_up(const arg_mouse& arg)
{
_m_emit(event_code::mouse_up, arg, &drawer_trigger::mouse_up);
}
void drawer::mouse_wheel(const arg_wheel& arg)
{
_m_emit(event_code::mouse_wheel, arg, &drawer_trigger::mouse_wheel);
}
void drawer::mouse_dropfiles(const arg_dropfiles& arg)
{
_m_emit(event_code::mouse_drop, arg, &drawer_trigger::mouse_dropfiles);
}
void drawer::resizing(const arg_resizing& arg)
{
_m_emit(event_code::resizing, arg, &drawer_trigger::resizing);
}
void drawer::resized(const arg_resized& arg)
{
_m_emit(event_code::resized, arg, &drawer_trigger::resized);
}
void drawer::move(const arg_move& arg)
{
_m_emit(event_code::move, arg, &drawer_trigger::move);
}
void drawer::focus(const arg_focus& arg)
{
_m_emit(event_code::focus, arg, &drawer_trigger::focus);
}
void drawer::key_press(const arg_keyboard& arg)
{
_m_emit(event_code::key_press, arg, &drawer_trigger::key_press);
}
void drawer::key_char(const arg_keyboard& arg)
{
_m_emit(event_code::key_char, arg, &drawer_trigger::key_char);
}
void drawer::key_release(const arg_keyboard& arg)
{
_m_emit(event_code::key_release, arg, &drawer_trigger::key_release);
}
void drawer::shortkey(const arg_keyboard& arg)
{
_m_emit(event_code::shortkey, arg, &drawer_trigger::shortkey);
}
void drawer::map(window wd) //Copy the root buffer to screen
{
if(wd)
{
bedrock_type::core_window_t* iwd = reinterpret_cast<bedrock_type::core_window_t*>(wd);
bedrock_type::core_window_t * caret_wd = iwd->root_widget->other.attribute.root->focus;
bool owns_caret = (caret_wd && (caret_wd->together.caret) && (caret_wd->together.caret->visible()));
//The caret in X11 is implemented by Nana, it is different from Windows'
//the caret in X11 is asynchronous, it is hard to hide and show the caret
//immediately, and therefore the caret always be flickering when the graphics
//buffer is mapping to the window.
if(owns_caret)
{
#ifndef NANA_X11
caret_wd->together.caret->visible(false);
#else
owns_caret = nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, false);
#endif
}
if(false == edge_nimbus_renderer_t::instance().render(iwd))
{
nana::rectangle vr;
if(bedrock_type::window_manager_t::wndlayout_type::read_visual_rectangle(iwd, vr))
iwd->root_graph->paste(iwd->root, vr, vr.x, vr.y);
}
if(owns_caret)
{
#ifndef NANA_X11
caret_wd->together.caret->visible(true);
#else
nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, true);
#endif
}
}
}
void drawer::refresh()
{
if(realizer_ && (refreshing_ == false))
{
refreshing_ = true;
_m_bground_pre();
realizer_->refresh(graphics);
_m_draw_dynamic_drawing_object();
_m_bground_end();
graphics.flush();
refreshing_ = false;
}
}
drawer_trigger* drawer::realizer() const
{
return realizer_;
}
void drawer::attached(widget& wd, drawer_trigger& realizer)
{
for (auto i = std::begin(mth_state_), end = std::end(mth_state_); i != end; ++i)
*i = method_state::unknown;
realizer_ = &realizer;
realizer.attached(wd, graphics);
}
drawer_trigger* drawer::detached()
{
if(realizer_)
{
auto rmp = realizer_;
realizer_ = nullptr;
rmp->detached();
return rmp;
}
return nullptr;
}
void drawer::clear()
{
std::vector<dynamic_drawing::object*> then;
for(auto p : dynamic_drawing_objects_)
{
if(p->diehard())
then.push_back(p);
else
delete p;
}
then.swap(dynamic_drawing_objects_);
}
void* drawer::draw(std::function<void(paint::graphics&)> && f, bool diehard)
{
if(f)
{
auto p = new dynamic_drawing::user_draw_function(std::move(f), diehard);
dynamic_drawing_objects_.push_back(p);
return (diehard ? p : nullptr);
}
return nullptr;
}
void drawer::erase(void * p)
{
if(p)
{
auto i = std::find(dynamic_drawing_objects_.begin(), dynamic_drawing_objects_.end(), p);
if (i != dynamic_drawing_objects_.end())
{
delete (*i);
dynamic_drawing_objects_.erase(i);
}
}
}
void drawer::_m_bground_pre()
{
if(core_window_->effect.bground && core_window_->effect.bground_fade_rate < 0.01)
core_window_->other.glass_buffer.paste(graphics, 0, 0);
}
void drawer::_m_bground_end()
{
if(core_window_->effect.bground && core_window_->effect.bground_fade_rate >= 0.01)
core_window_->other.glass_buffer.blend(core_window_->other.glass_buffer.size(), graphics, nana::point(), core_window_->effect.bground_fade_rate);
}
void drawer::_m_draw_dynamic_drawing_object()
{
for(auto * dw : dynamic_drawing_objects_)
dw->draw(graphics);
}
//If the drawer_trigger didn't declear a lazy refresh, then use the refresh().
void drawer::_m_use_refresh()
{
if (basic_window::update_state::refresh != core_window_->other.upd_state)
refresh();
}
}//end namespace detail
}//end namespace nana

View File

@@ -0,0 +1,45 @@
/*
* The Store for the Storage Of Elements
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/detail/element_store.cpp
*/
#include <nana/gui/detail/element_store.hpp>
namespace nana
{
namespace detail
{
//class element_store
element_store::data::data()
: fast_ptr(nullptr)
{}
nana::element::element_interface * const * element_store::bground(const std::string& name)
{
element_interface * const * addr = &(bground_.table[name].fast_ptr);
return addr;
}
void element_store::bground(const std::string& name, const pat::cloneable<element_interface>& rhs)
{
auto & store = bground_.table[name];
store.object = rhs;
store.fast_ptr = store.object.get();
}
void element_store::bground(const std::string& name, pat::cloneable<element_interface>&& rv)
{
auto & store = bground_.table[name];
store.object = std::move(rv);
store.fast_ptr = store.object.get();
}
}//end namespace detail
}

View File

@@ -0,0 +1,52 @@
#include <nana/gui/detail/events_operation.hpp>
namespace nana
{
namespace detail
{
//class events_operation
typedef std::lock_guard<std::recursive_mutex> lock_guard;
void events_operation::make(window wd, const std::shared_ptr<general_events>& sp)
{
lock_guard lock(mutex_);
evt_table_[wd] = sp;
}
void events_operation::umake(window wd)
{
lock_guard lock(mutex_);
evt_table_.erase(wd);
}
void events_operation::register_evt(event_handle evt)
{
lock_guard lock(mutex_);
register_.insert(evt);
}
void events_operation::cancel(event_handle evt)
{
lock_guard lock(mutex_);
register_.erase(evt);
}
void events_operation::erase(event_handle evt)
{
lock_guard lock(mutex_);
auto i = register_.find(evt);
if (i != register_.end())
{
reinterpret_cast<detail::docker_interface*>(evt)->get_event()->remove(evt);
}
}
std::size_t events_operation::size() const
{
lock_guard lock(mutex_);
return register_.size();
}
//end namespace events_operation
}//end namespace detail
}//end namespace nana

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
/*
* Window Layout Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/detail/window_layout.hpp
*
*/
#include <nana/gui/detail/window_layout.hpp>
#include <nana/gui/detail/basic_window.hpp>
#include <nana/gui/detail/native_window_interface.hpp>
#include <nana/gui/layout_utility.hpp>
#include <algorithm>
namespace nana
{
namespace detail
{
//class window_layout
void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed)
{
if (nullptr == wd->effect.bground)
{
if (is_redraw)
{
if (wd->flags.refreshing) return;
wd->flags.refreshing = true;
wd->drawer.refresh();
wd->flags.refreshing = false;
}
maproot(wd, is_redraw, is_child_refreshed);
}
else
_m_paint_glass_window(wd, is_redraw, is_child_refreshed, false, true);
}
bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool is_child_refreshed)
{
nana::rectangle vr;
if (read_visual_rectangle(wd, vr))
{
//get the root graphics
auto& graph = *(wd->root_graph);
if (wd->other.category != category::lite_widget_tag::value)
graph.bitblt(vr, wd->drawer.graphics, nana::point(vr.x - wd->pos_root.x, vr.y - wd->pos_root.y));
_m_paste_children(wd, is_child_refreshed, have_refreshed, vr, graph, nana::point());
if (wd->parent)
{
std::vector<wd_rectangle> blocks;
blocks.reserve(10);
if (read_overlaps(wd, vr, blocks))
{
nana::point p_src;
for (auto & el : blocks)
{
if (el.window->other.category == category::frame_tag::value)
{
native_window_type container = el.window->other.attribute.frame->container;
native_interface::refresh_window(container);
graph.bitblt(el.r, container);
}
else
{
p_src.x = el.r.x - el.window->pos_root.x;
p_src.y = el.r.y - el.window->pos_root.y;
graph.bitblt(el.r, (el.window->drawer.graphics), p_src);
}
_m_paste_children(el.window, is_child_refreshed, false, el.r, graph, nana::point{});
}
}
}
_m_notify_glasses(wd, vr);
return true;
}
return false;
}
void window_layout::paste_children_to_graphics(core_window_t* wd, nana::paint::graphics& graph)
{
_m_paste_children(wd, false, false, rectangle{ wd->pos_root, wd->dimension }, graph, wd->pos_root);
}
//read_visual_rectangle
//@brief: Reads the visual rectangle of a window, the visual rectangle's reference frame is to root widget,
// the visual rectangle is a rectangular block that a window should be displayed on screen.
// The result is a rectangle that is a visible area for its ancesters.
bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual)
{
if (false == wd->visible) return false;
visual = rectangle{ wd->pos_root, wd->dimension };
if (wd->root_widget != wd)
{
//Test if the root widget is overlapped the specified widget
//the pos of root widget is (0, 0)
if (overlap(visual, rectangle{ wd->root_widget->pos_owner, wd->root_widget->dimension }) == false)
return false;
}
for (auto* parent = wd->parent; parent; parent = parent->parent)
{
overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual);
}
return true;
}
//read_overlaps
// reads the overlaps that are overlapped a rectangular block
bool window_layout::read_overlaps(core_window_t* wd, const nana::rectangle& vis_rect, std::vector<wd_rectangle>& blocks)
{
wd_rectangle block;
while (wd->parent)
{
auto & siblings = wd->parent->children;
//It should be checked that whether the window is still a chlid of its parent.
if (siblings.size())
{
auto i = &(siblings[0]);
auto *end = i + siblings.size();
i = std::find(i, end, wd);
if (i != end)
{
//find the widget that next to wd.
for (++i; i < end; ++i)
{
core_window_t* cover = *i;
if (cover->visible && (nullptr == cover->effect.bground))
{
if (overlap(vis_rect, rectangle{ cover->pos_root, cover->dimension }, block.r))
{
block.window = cover;
blocks.push_back(block);
}
}
}
}
}
wd = wd->parent;
}
return (!blocks.empty());
}
bool window_layout::enable_effects_bground(core_window_t * wd, bool enabled)
{
if (wd->other.category != category::widget_tag::value)
return false;
if (false == enabled)
{
delete wd->effect.bground;
wd->effect.bground = nullptr;
wd->effect.bground_fade_rate = 0;
}
//Find the window whether it is registered for the bground effects
auto i = std::find(data_sect.effects_bground_windows.begin(), data_sect.effects_bground_windows.end(), wd);
if (i != data_sect.effects_bground_windows.end())
{
//If it has already registered, do nothing.
if (enabled)
return false;
//Disable the effect.
data_sect.effects_bground_windows.erase(i);
wd->other.glass_buffer.release();
return true;
}
//No such effect has registered.
if (false == enabled)
return false;
//Enable the effect.
data_sect.effects_bground_windows.push_back(wd);
wd->other.glass_buffer.make(wd->dimension.width, wd->dimension.height);
make_bground(wd);
return true;
}
//make_bground
// update the glass buffer of a glass window.
void window_layout::make_bground(core_window_t* const wd)
{
nana::point rpos{ wd->pos_root };
auto & glass_buffer = wd->other.glass_buffer;
if (wd->parent->other.category == category::lite_widget_tag::value)
{
std::vector<core_window_t*> layers;
core_window_t * beg = wd->parent;
while (beg && (beg->other.category == category::lite_widget_tag::value))
{
layers.push_back(beg);
beg = beg->parent;
}
glass_buffer.bitblt(wd->dimension, beg->drawer.graphics, wd->pos_root - beg->pos_root);
nana::rectangle r(wd->pos_owner, wd->dimension);
for (auto i = layers.rbegin(), layers_rend = layers.rend(); i != layers_rend; ++i)
{
core_window_t * pre = *i;
if (false == pre->visible)
continue;
core_window_t * term = ((i + 1 != layers_rend) ? *(i + 1) : wd);
r.x = wd->pos_root.x - pre->pos_root.x;
r.y = wd->pos_root.y - pre->pos_root.y;
for (auto child : pre->children)
{
if (child->index >= term->index)
break;
nana::rectangle ovlp;
if (child->visible && overlap(r, rectangle(child->pos_owner, child->dimension), ovlp))
{
if (child->other.category != category::lite_widget_tag::value)
glass_buffer.bitblt(nana::rectangle(ovlp.x - pre->pos_owner.x, ovlp.y - pre->pos_owner.y, ovlp.width, ovlp.height), child->drawer.graphics, nana::point(ovlp.x - child->pos_owner.x, ovlp.y - child->pos_owner.y));
ovlp.x += pre->pos_root.x;
ovlp.y += pre->pos_root.y;
_m_paste_children(child, false, false, ovlp, glass_buffer, rpos);
}
}
}
}
else
glass_buffer.bitblt(wd->dimension, wd->parent->drawer.graphics, wd->pos_owner);
const rectangle r_of_wd{ wd->pos_owner, wd->dimension };
for (auto child : wd->parent->children)
{
if (child->index >= wd->index)
break;
nana::rectangle ovlp;
if (child->visible && overlap(r_of_wd, rectangle{ child->pos_owner, child->dimension }, ovlp))
{
if (child->other.category != category::lite_widget_tag::value)
glass_buffer.bitblt(nana::rectangle{ ovlp.x - wd->pos_owner.x, ovlp.y - wd->pos_owner.y, ovlp.width, ovlp.height }, child->drawer.graphics, nana::point(ovlp.x - child->pos_owner.x, ovlp.y - child->pos_owner.y));
ovlp.x += wd->pos_root.x;
ovlp.y += wd->pos_root.y;
_m_paste_children(child, false, false, ovlp, glass_buffer, rpos);
}
}
if (wd->effect.bground)
wd->effect.bground->take_effect(reinterpret_cast<window>(wd), glass_buffer);
}
//_m_paste_children
//@brief:paste children window to the root graphics directly. just paste the visual rectangle
void window_layout::_m_paste_children(core_window_t* wd, bool is_child_refreshed, bool have_refreshed, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos)
{
nana::rectangle rect;
for (auto child : wd->children)
{
//it will not past children if no drawer and visible is false.
if ((false == child->visible) || ((child->other.category != category::lite_widget_tag::value) && child->drawer.graphics.empty()))
continue;
if (nullptr == child->effect.bground)
{
if (overlap(nana::rectangle{ child->pos_root, child->dimension }, parent_rect, rect))
{
bool have_child_refreshed = false;
if (child->other.category != category::lite_widget_tag::value)
{
if (is_child_refreshed && (false == child->flags.refreshing))
{
have_child_refreshed = true;
child->flags.refreshing = true;
child->drawer.refresh();
child->flags.refreshing = false;
}
graph.bitblt(nana::rectangle(rect.x - graph_rpos.x, rect.y - graph_rpos.y, rect.width, rect.height),
child->drawer.graphics, nana::point(rect.x - child->pos_root.x, rect.y - child->pos_root.y));
}
_m_paste_children(child, is_child_refreshed, have_child_refreshed, rect, graph, graph_rpos);
}
}
else
{
//If have_refreshed, the glass should be notified.
_m_paint_glass_window(child, false, is_child_refreshed, have_refreshed, false);
}
}
}
void window_layout::_m_paint_glass_window(core_window_t* wd, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other)
{
if (wd->flags.refreshing && is_redraw) return;
nana::rectangle vr;
if (read_visual_rectangle(wd, vr))
{
if (is_redraw || called_by_notify)
{
if (called_by_notify)
make_bground(wd);
wd->flags.refreshing = true;
wd->drawer.refresh();
wd->flags.refreshing = false;
}
auto & root_graph = *(wd->root_graph);
//Map root
root_graph.bitblt(vr, wd->drawer.graphics, nana::point(vr.x - wd->pos_root.x, vr.y - wd->pos_root.y));
_m_paste_children(wd, is_child_refreshed, (is_redraw || called_by_notify), vr, root_graph, nana::point());
if (wd->parent)
{
std::vector<wd_rectangle> blocks;
read_overlaps(wd, vr, blocks);
for (auto & n : blocks)
{
root_graph.bitblt(n.r, (n.window->drawer.graphics), nana::point(n.r.x - n.window->pos_root.x, n.r.y - n.window->pos_root.y));
}
}
if (notify_other)
_m_notify_glasses(wd, vr);
}
}
//_m_notify_glasses
//@brief: Notify the glass windows that are overlapped with the specified vis_rect
void window_layout::_m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& r_visual)
{
typedef category::flags cat_flags;
nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension);
for (auto wd : data_sect.effects_bground_windows)
{
if (wd == sigwd || !wd->visible || !wd->visible_parents() ||
(false == overlap(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd)))
continue;
if (sigwd->parent == wd->parent)
{
if (sigwd->index >= wd->index)
continue;
}
else if (sigwd != wd->parent)
{
if (wd->parent && (cat_flags::lite_widget == wd->parent->other.category))
{
//Test if sigwd is an ancestor of the glass window, and there are lite widgets
//between sigwd and glass window.
auto ancestor = wd->parent->parent;
while (ancestor && (ancestor != sigwd) && (cat_flags::lite_widget == ancestor->other.category))
ancestor = ancestor->parent;
if ((ancestor != sigwd) || (cat_flags::lite_widget == ancestor->other.category))
continue;
}
else
{
//test if sigwnd is a parent of glass window x, or a slibing of the glass window, or a child of the slibing of the glass window.
core_window_t *p = wd->parent, *signode = sigwd;
while (signode->parent && (signode->parent != p))
signode = signode->parent;
if ((!signode->parent) || (signode->index >= wd->index))
continue;
}
}
else
continue;
_m_paint_glass_window(wd, true, false, true, true);
}
}
//end class window_layout
window_layout::data_section window_layout::data_sect;
}//end namespace detail
}//end namespace nana

File diff suppressed because it is too large Load Diff

219
source/gui/dragger.cpp Normal file
View File

@@ -0,0 +1,219 @@
#include <nana/gui/dragger.hpp>
namespace nana
{
class dragger::dragger_impl_t
{
struct drag_target_t
{
window wd;
rectangle restrict_area;
arrange move_direction;
point origin;
drag_target_t(window w, const rectangle& r, arrange m)
: wd(w), restrict_area(r), move_direction(m)
{}
};
struct trigger_t
{
window wd;
event_handle press;
event_handle over;
event_handle release;
event_handle destroy;
};
public:
dragger_impl_t()
: dragging_(false)
{}
~dragger_impl_t()
{
_m_clear_triggers();
}
void drag_target(window wd, const rectangle& restrict_area, arrange arg)
{
for (auto & td : targets_)
{
if (td.wd == wd)
{
td.restrict_area = restrict_area;
td.move_direction = arg;
return;
}
}
targets_.emplace_back(wd, restrict_area, arg);
}
void remove_target(window wd)
{
for (auto i = targets_.begin(); i != targets_.end(); ++i)
{
if (i->wd == wd)
{
targets_.erase(i);
return;
}
}
}
void trigger(window wd)
{
trigger_t tg;
tg.wd = wd;
auto fn = std::bind(&dragger_impl_t::_m_trace, this, std::placeholders::_1);
auto & events = API::events(wd);
tg.press = events.mouse_down.connect(fn);
tg.over = events.mouse_move.connect(fn);
tg.release = events.mouse_up.connect(fn);
tg.destroy = events.destroy.connect([this](const arg_destroy& arg){
_m_destroy(arg.window_handle);
});
triggers_.push_back(tg);
}
private:
void _m_clear_triggers()
{
for(auto & t : triggers_)
{
API::umake_event(t.press);
API::umake_event(t.over);
API::umake_event(t.release);
API::umake_event(t.destroy);
API::capture_window(t.wd, false);
}
triggers_.clear();
}
void _m_destroy(::nana::window wd)
{
for(auto i = triggers_.begin(), end = triggers_.end(); i != end; ++i)
{
if(i->wd == wd)
{
triggers_.erase(i);
API::capture_window(wd, false);
return;
}
}
}
void _m_check_restrict_area(nana::point & pos, const nana::size & size, const nana::rectangle& restr_area)
{
if ((pos.x > 0) && (static_cast<int>(size.width) + pos.x > restr_area.right()))
pos.x = restr_area.right() - static_cast<int>(size.width);
if (pos.x < restr_area.x)
pos.x = restr_area.x;
if ((pos.y > 0) && (static_cast<int>(size.height) + pos.y > restr_area.bottom()))
pos.y = restr_area.bottom() - static_cast<int>(size.height);
if (pos.y < restr_area.y)
pos.y = restr_area.y;
}
void _m_trace(const arg_mouse& arg)
{
switch(arg.evt_code)
{
case event_code::mouse_down:
dragging_ = true;
API::capture_window(arg.window_handle, true);
origin_ = API::cursor_position();
for(auto & t : targets_)
{
t.origin = API::window_position(t.wd);
window owner = API::get_owner_window(t.wd);
if(owner)
API::calc_screen_point(owner, t.origin);
}
break;
case event_code::mouse_move:
if(dragging_ && arg.left_button)
{
auto pos = API::cursor_position();
pos -= origin_;
for(auto & t : targets_)
{
if(API::is_window_zoomed(t.wd, true) == false)
{
auto owner = API::get_owner_window(t.wd);
auto wdps = t.origin;
if (owner)
API::calc_window_point(owner, wdps);
switch (t.move_direction)
{
case nana::arrange::horizontal:
wdps.x += pos.x;
break;
case nana::arrange::vertical:
wdps.y += pos.y;
break;
default:
wdps += pos;
}
if (!t.restrict_area.empty())
_m_check_restrict_area(wdps, API::window_size(t.wd), t.restrict_area);
API::move_window(t.wd, wdps.x, wdps.y);
}
}
}
break;
case event_code::mouse_up:
API::capture_window(arg.window_handle, false);
dragging_ = false;
break;
default:
break;
}
}
private:
bool dragging_;
nana::point origin_;
std::vector<drag_target_t> targets_;
std::vector<trigger_t> triggers_;
};
//class dragger
dragger::dragger()
: impl_(new dragger_impl_t)
{
}
dragger::~dragger()
{
delete impl_;
}
void dragger::target(window wd)
{
impl_->drag_target(wd, rectangle(), nana::arrange::horizontal_vertical);
}
void dragger::target(window wd, const rectangle& restrict_area, nana::arrange arg)
{
impl_->drag_target(wd, restrict_area, arg);
}
void dragger::remove_target(window wd)
{
impl_->remove_target(wd);
}
void dragger::trigger(window tg)
{
impl_->trigger(tg);
}
//end class dragger
}//end namespace nana

86
source/gui/drawing.cpp Normal file
View File

@@ -0,0 +1,86 @@
/*
* A Drawing Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/drawing.cpp
*/
#include <nana/gui/drawing.hpp>
#include <nana/gui/programming_interface.hpp>
#include <nana/gui/detail/basic_window.hpp>
namespace nana
{
//restrict
//@brief: This name is only visible for this compiling-unit
namespace restrict
{
typedef detail::bedrock::core_window_t core_window_t;
extern detail::bedrock& bedrock;
inline detail::drawer& get_drawer(window wd)
{
return reinterpret_cast<core_window_t*>(wd)->drawer;
}
}
//class drawing
drawing::drawing(window wd)
:handle_(wd)
{}
drawing::~drawing(){} //Just for polymorphism
bool drawing::empty() const
{
return API::empty_window(handle_) || reinterpret_cast<restrict::core_window_t*>(handle_)->root_graph->empty();
}
void drawing::update() const
{
API::refresh_window(handle_);
}
void drawing::draw(const draw_fn_t& f)
{
if(API::empty_window(handle_)) return;
restrict::get_drawer(handle_).draw(draw_fn_t(f), false);
}
void drawing::draw(draw_fn_t&& f)
{
if(API::empty_window(handle_)) return;
restrict::get_drawer(handle_).draw(std::move(f), false);
}
drawing::diehard_t drawing::draw_diehard(const draw_fn_t& f)
{
if(API::empty_window(handle_)) return nullptr;
return reinterpret_cast<diehard_t>(restrict::get_drawer(handle_).draw(draw_fn_t(f), true));
}
drawing::diehard_t drawing::draw_diehard(draw_fn_t&& f)
{
if(API::empty_window(handle_)) return nullptr;
return reinterpret_cast<diehard_t>(restrict::get_drawer(handle_).draw(std::move(f), true));
}
void drawing::erase(diehard_t d)
{
if(API::empty_window(handle_))
restrict::get_drawer(handle_).erase(d);
}
void drawing::clear()
{
if(API::empty_window(handle_)) return;
restrict::get_drawer(handle_).clear();
}
//end class drawing
}//end namespace nana

78
source/gui/effects.cpp Normal file
View File

@@ -0,0 +1,78 @@
#include <nana/gui/effects.hpp>
#include <nana/gui/programming_interface.hpp>
namespace nana
{
namespace effects
{
bground_interface::~bground_interface()
{}
bground_factory_interface::~bground_factory_interface()
{}
//Here defines some effect implementations.
namespace impl
{
class transparent
: public bground_interface
{
public:
transparent(std::size_t percent)
: fade_rate_( percent <= 100 ? double(percent) / 100.0 : 0)
{}
void take_effect(window wd, graph_reference graph) const
{
if(fade_rate_ < 0.001)
return;
nana::color_t color = API::background(wd);
graph.blend(graph.size(), color, fade_rate_);
}
private:
const double fade_rate_;
};
class blur
: public bground_interface
{
public:
blur(std::size_t radius)
:radius_(radius)
{}
void take_effect(window, graph_reference graph) const
{
graph.blur(graph.size(), radius_);
}
private:
const std::size_t radius_;
};
}//end namespace impl
//class bground_transparent
bground_transparent::bground_transparent(std::size_t percent)
: percent_(percent)
{}
bground_interface* bground_transparent::create() const
{
return new impl::transparent(percent_);
}
//end class bgroun_transparent
//class bground_blur
bground_blur::bground_blur(std::size_t radius)
: radius_(radius)
{}
bground_interface * bground_blur::create() const
{
return new impl::blur(radius_);
}
//end class bground_blur
}
}//end namespace nana

820
source/gui/element.cpp Normal file
View File

@@ -0,0 +1,820 @@
#include <nana/gui/element.hpp>
#include <nana/gui/detail/bedrock.hpp>
#include <nana/gui/detail/element_store.hpp>
#include <nana/paint/image.hpp>
#include <map>
#include <string>
#if defined(STD_THREAD_NOT_SUPPORTED)
#include <nana/std_mutex.hpp>
#else
#include <mutex>
#endif
namespace nana
{
//Element definitions
namespace element
{
class crook
: public crook_interface
{
bool draw(graph_reference graph, nana::color_t bgcolor, nana::color_t fgcolor, const nana::rectangle& r, element_state es, const data& crook_data) override
{
if(crook_data.radio)
{
unsigned bmp_unchecked[12][12] = {
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCFD0D0, 0xAAABAB, 0x919292, 0x919292, 0xAAABAB, 0xCFD0D0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xA3A4A4, 0xB9BABA, 0xDBDBDB, 0xF2F2F2, 0xF2F2F2, 0xDBDBDB, 0xB9BABA, 0xA3A4A4, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xEDEDEE, 0xC6C9CD, 0xB5BABF, 0xB5BABF, 0xC8CBCE, 0xEDEEEE, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF},
{0xCFD0D0, 0xB9BABA, 0xE9EAEB, 0xB3B8BD, 0xBDC2C7, 0xC8CDD2, 0xC9CED3, 0xC5C8CC, 0xBEC1C5, 0xEBECEC, 0xB9BABA, 0xCFD0D0},
{0xA9A9A9, 0xDCDCDC, 0xC5C8CC, 0xBEC3C9, 0xCBCFD5, 0xCED2D7, 0xD5D8DC, 0xDCDEE0, 0xD3D4D7, 0xD4D5D5, 0xDCDCDC, 0xA9A9A9},
{0x919292, 0xF2F2F2, 0xB4B9BD, 0xCDD1D6, 0xD3D6DA, 0xDBDDDF, 0xE4E4E5, 0xE9E9E9, 0xE8E8E9, 0xD0D1D2, 0xF2F2F2, 0x919292},
{0x919292, 0xF2F2F2, 0xBBBEC2, 0xD7DADD, 0xE0E1E3, 0xE9E9E9, 0xEFEFEF, 0xF0F0F0, 0xEFEFF0, 0xDBDCDC, 0xEFEFEF, 0x939494},
{0xA7A8A8, 0xDDDDDD, 0xCFD1D3, 0xD5D6D8, 0xE9E9E9, 0xEFEFEF, 0xF4F4F4, 0xF5F5F5, 0xEEEEEE, 0xE8E8E8, 0xDDDDDD, 0xA7A8A8},
{0xCECECE, 0xBABBBB, 0xECECED, 0xCDCECF, 0xE1E2E2, 0xF0F0F0, 0xF4F4F4, 0xF1F1F1, 0xEBEBEB, 0xF2F2F2, 0xBABBBB, 0xCECECE},
{0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xF0F0F1, 0xE2E3E3, 0xE4E4E5, 0xE9EAEA, 0xEEEEEF, 0xF3F3F3, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xA2A3A3, 0xBABBBB, 0xDBDBDB, 0xF4F4F4, 0xF4F4F4, 0xDCDCDC, 0xBABBBB, 0xA2A3A3, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCECECE, 0xAAABAB, 0x8E8F8F, 0x8E8F8F, 0xA9A9A9, 0xCECECE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}
};
unsigned bmp_unchecked_highlight[12][12] = {
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB7CCD8, 0x7FA4BA, 0x5989A5, 0x5989A5, 0x7FA4BA, 0xB7CCD8, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x759DB4, 0x8FB7C8, 0xBCDDE5, 0xDBF6F8, 0xDBF6F8, 0xBCDDE5, 0x8FB7C8, 0x759DB4, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD3F4FA, 0x9BD7F9, 0x84CBF9, 0x84CBF9, 0x9CD8F9, 0xD4F4FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF},
{0xB7CCD8, 0x8FB7C8, 0xCFF1FA, 0x80CAF9, 0x96D3FB, 0xAADDFD, 0xABDDFD, 0x9AD5FB, 0x86CEF9, 0xCFF2FA, 0x8FB7C8, 0xB7CCD8},
{0x7DA2B9, 0xBEDEE6, 0x9AD7F9, 0x98D5FB, 0xB1DFFD, 0xB2E0FD, 0xB7E3FD, 0xBCE5FD, 0xA6DCFB, 0xA1DCF9, 0xBEDEE6, 0x7DA2B9},
{0x5989A5, 0xDBF6F8, 0x80CAF9, 0xAFDEFD, 0xB6E2FD, 0xBBE5FD, 0xC1E8FD, 0xC5EAFD, 0xC7EBFD, 0x99D8FA, 0xDBF6F8, 0x5989A5},
{0x5989A5, 0xDBF6F8, 0x84CDF9, 0xB6E2FD, 0xBFE7FD, 0xC7EBFD, 0xD5F0FE, 0xDAF2FE, 0xD8F1FE, 0xB1E1FB, 0xD8F4F6, 0x5D8CA7},
{0x7BA1B8, 0xBFDFE7, 0x9FDBF9, 0xA7DDFB, 0xC8EBFD, 0xD6F1FE, 0xE2F5FE, 0xE5F6FE, 0xD8F0FD, 0xCAEDFB, 0xBFDFE7, 0x7BA1B8},
{0xB5CAD7, 0x91B8C9, 0xCFF2FA, 0x92D5F9, 0xBAE5FC, 0xDAF2FE, 0xE4F5FE, 0xDFF3FE, 0xD2EEFD, 0xDBF7FA, 0x91B8C9, 0xB5CAD7},
{0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD7F6FA, 0xBDE8FB, 0xC2E8FC, 0xD0EDFD, 0xD7F2FC, 0xDDF8FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x739BB3, 0x91B8C9, 0xBCDDE5, 0xDEF9FA, 0xDEF9FA, 0xBEDEE6, 0x91B8C9, 0x739BB3, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB5CAD7, 0x7FA4BA, 0x5586A3, 0x5586A3, 0x7DA2B9, 0xB5CAD7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}
};
unsigned bmp_checked[12][12] = {
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCFD0D0, 0xAAABAB, 0x919292, 0x919292, 0xAAABAB, 0xCFD0D0, 0xFCFCFC, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xA3A4A4, 0xB9BABA, 0xDBDBDB, 0xF2F2F2, 0xF2F2F2, 0xDBDBDB, 0xB9BABA, 0xA3A4A4, 0xF3F3F3, 0xFFFFFF},
{0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xEDEDEE, 0xBABFC5, 0x85939F, 0x85939F, 0xBCC1C5, 0xEDEEEE, 0xC3C3C3, 0xA2A3A3, 0xFCFCFC},
{0xCFD0D0, 0xB9BABA, 0xE9EAEB, 0x8997A2, 0x274760, 0x486378, 0x365166, 0x204058, 0x8E9AA4, 0xEBECEC, 0xB9BABA, 0xCFD0D0},
{0xA9A9A9, 0xDCDCDC, 0xB9BEC4, 0x24445D, 0x91B2C6, 0xC7EBFD, 0x69C2D4, 0x14405C, 0x1E3F57, 0xC9CCCD, 0xDCDCDC, 0xA9A9A9},
{0x919292, 0xF2F2F2, 0x7D8D98, 0x304B5F, 0x90D5E5, 0x5DCEDD, 0x28A2D1, 0x178AC7, 0x183348, 0x8F9CA6, 0xF2F2F2, 0x919292},
{0x919292, 0xF2F2F2, 0x82909C, 0x183347, 0x228FC6, 0x209DD1, 0x1898D1, 0x0E84C6, 0x183348, 0x97A3AC, 0xEFEFEF, 0x939494},
{0xA7A8A8, 0xDDDDDD, 0xC0C5C9, 0x1E3F57, 0x0F3F5D, 0x0F83C7, 0x0B82C7, 0x0C3D5D, 0x1F3F58, 0xD9DCDE, 0xDDDDDD, 0xA7A8A8},
{0xCECECE, 0xBABBBB, 0xECECED, 0x99A3AB, 0x1D3E57, 0x18354A, 0x19344A, 0x1E3E57, 0xAEB8BF, 0xF2F2F2, 0xBABBBB, 0xCECECE},
{0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xF0F0F1, 0xD1D5D7, 0xA6B0B9, 0xA9B4BC, 0xDCDFE2, 0xF3F3F3, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xA2A3A3, 0xBABBBB, 0xDBDBDB, 0xF4F4F4, 0xF4F4F4, 0xDCDCDC, 0xBABBBB, 0xA2A3A3, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCECECE, 0xAAABAB, 0x8E8F8F, 0x8E8F8F, 0xA9A9A9, 0xCECECE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}
};
unsigned bmp_checked_highlight[12][12] = {
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB7CCD8, 0x7FA4BA, 0x5989A5, 0x5989A5, 0x7FA4BA, 0xB7CCD8, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x759DB4, 0x8FB7C8, 0xBCDDE5, 0xDBF6F8, 0xDBF6F8, 0xBCDDE5, 0x8FB7C8, 0x759DB4, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD3F4FA, 0x92CCED, 0x639FC7, 0x639FC7, 0x93CDED, 0xD4F4FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF},
{0xB7CCD8, 0x8FB7C8, 0xCFF1FA, 0x66A3CC, 0x264862, 0x47647A, 0x355268, 0x1E405A, 0x66A3C9, 0xCFF2FA, 0x8FB7C8, 0xB7CCD8},
{0x7DA2B9, 0xBEDEE6, 0x91CCED, 0x22445E, 0x9DBBCD, 0xE9F7FE, 0x7FE6EE, 0x154664, 0x1D3F58, 0x99D3EF, 0xBEDEE6, 0x7DA2B9},
{0x5989A5, 0xDBF6F8, 0x5C98BF, 0x2F4B60, 0xB1F6FA, 0x74FFFF, 0x32CAFF, 0x1DAAF3, 0x173348, 0x6CA1C0, 0xDBF6F8, 0x5989A5},
{0x5989A5, 0xDBF6F8, 0x5E99BF, 0x173348, 0x2AB0F2, 0x28C4FF, 0x1EBEFF, 0x11A3F2, 0x173348, 0x7BA6C0, 0xD8F4F6, 0x5D8CA7},
{0x7BA1B8, 0xBFDFE7, 0x94CEEB, 0x1D3F58, 0x114567, 0x13A2F3, 0x0DA0F3, 0x0D4367, 0x1E3F58, 0xBEE0EF, 0xBFDFE7, 0x7BA1B8},
{0xB5CAD7, 0x91B8C9, 0xCFF2FA, 0x6FA8C9, 0x1C3E58, 0x18354B, 0x18354B, 0x1D3E58, 0x9CBACC, 0xDBF7FA, 0x91B8C9, 0xB5CAD7},
{0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD7F6FA, 0xAFDAED, 0x8EB3C9, 0x98B7CA, 0xC7E3EE, 0xDDF8FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x739BB3, 0x91B8C9, 0xBCDDE5, 0xDEF9FA, 0xDEF9FA, 0xBEDEE6, 0x91B8C9, 0x739BB3, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB5CAD7, 0x7FA4BA, 0x5586A3, 0x5586A3, 0x7DA2B9, 0xB5CAD7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}
};
unsigned bmp_checked_press[12][12] = {
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xA6BDCE, 0x6089A8, 0x31668E, 0x31668E, 0x6089A8, 0xA6BDCE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x5480A1, 0x6C99B8, 0x9DC4DC, 0xBEE1F3, 0xBEE1F3, 0x9DC4DC, 0x6C99B8, 0x5480A1, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0x517E9F, 0x7AA5C2, 0xB6DDF3, 0x73B2D6, 0x4A8AB0, 0x4A8AB0, 0x74B3D8, 0xB7DEF3, 0x7AA5C2, 0x517E9F, 0xFFFFFF},
{0xA6BDCE, 0x6C99B8, 0xB1DBF1, 0x4B8DB4, 0x244660, 0x456279, 0x335167, 0x1D3F59, 0x4C90B7, 0xB1DCF3, 0x6C99B8, 0xA6BDCE},
{0x5E87A6, 0x9FC5DD, 0x71B1D6, 0x21435D, 0x7EA5BC, 0x95D9FC, 0x478BAE, 0x113858, 0x1B3E58, 0x78BDE2, 0x9FC5DD, 0x5E87A6},
{0x31668E, 0xBEE1F3, 0x4484AA, 0x2E4A60, 0x5DA2C6, 0x3A84AA, 0x19658D, 0x0F5984, 0x153248, 0x5794B7, 0xBEE1F3, 0x31668E},
{0x31668E, 0xBEE1F3, 0x4687AE, 0x153247, 0x165D84, 0x14628D, 0x0F5F8D, 0x095684, 0x163248, 0x6B9DB9, 0xBBDEF1, 0x366990},
{0x5B85A5, 0xA0C7DE, 0x74B7DC, 0x1B3E58, 0x0D3659, 0x0A5583, 0x075483, 0x0A3459, 0x1E3F58, 0xA9D2E9, 0xA0C7DE, 0x5B85A5},
{0xA3BBCD, 0x6D9BBA, 0xB2DDF3, 0x5599BE, 0x1C3E57, 0x17344A, 0x17344B, 0x1D3E57, 0x91B3C7, 0xC1E4F6, 0x6D9BBA, 0xA3BBCD},
{0xFFFFFF, 0x517E9F, 0x7AA5C2, 0xBBE1F5, 0x98CAE4, 0x80AAC3, 0x8CAFC5, 0xB7D7EA, 0xC2E4F6, 0x7AA5C2, 0x517E9F, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0x517E9F, 0x6D9BBA, 0x9DC4DC, 0xC2E4F6, 0xC2E4F6, 0x9FC5DD, 0x6D9BBA, 0x517E9F, 0xFFFFFF, 0xFFFFFF},
{0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xA3BBCD, 0x6089A8, 0x2C628B, 0x2C628B, 0x5E87A6, 0xA3BBCD, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}
};
unsigned (*colormap)[12][12] = &bmp_unchecked;
switch(es)
{
case element_state::normal:
case element_state::focus_normal:
colormap = (crook_data.check_state != state::unchecked ? &bmp_checked : &bmp_unchecked);
break;
case element_state::hovered:
case element_state::focus_hovered:
colormap = (crook_data.check_state != state::unchecked ? &bmp_checked_highlight : &bmp_unchecked_highlight);
break;
case element_state::pressed:
colormap = &bmp_checked_press;
break;
default:
break;
}
const int x = r.x + 2;
const int y = r.y + 2;
for(int top = 0; top < 12; ++top)
{
for(int left = 0; left < 12; ++left)
{
if((*colormap)[top][left] != 0xFFFFFF)
graph.set_pixel(left + x, top + y, (*colormap)[top][left]);
}
}
}
else
{
const nana::color_t highlighted = 0x5EB6F7;
switch(es)
{
case element_state::hovered:
case element_state::focus_hovered:
bgcolor = graph.mix(bgcolor, highlighted, 0.8);
fgcolor = graph.mix(fgcolor, highlighted, 0.8);
break;
case element_state::pressed:
bgcolor = graph.mix(bgcolor, highlighted, 0.4);
fgcolor = graph.mix(fgcolor, highlighted, 0.4);
break;
case element_state::disabled:
bgcolor = fgcolor = 0xB2B7BC;
break;
default:
//Leave things as they are
break;
}
const int x = r.x + 1;
const int y = r.y + 1;
graph.rectangle(x, y, 13, 13, fgcolor, false);
graph.rectangle(x + 1, y + 1, 11, 11, bgcolor, true);
switch(crook_data.check_state)
{
case state::checked:
{
int sx = x + 2;
int sy = y + 4;
for(int i = 0; i < 3; i++)
{
sx++;
sy++;
graph.line(sx, sy, sx, sy + 3, fgcolor);
}
for(int i = 0; i < 4; i++)
{
sx++;
sy--;
graph.line(sx, sy, sx, sy + 3, fgcolor);
}
}
break;
case state::partial:
graph.rectangle(x + 2, y + 2, 9, 9, fgcolor, true);
break;
default:
break;
}
}
return true;
}
}; //end class crook
class menu_crook
: public crook_interface
{
bool draw(graph_reference graph, nana::color_t, nana::color_t fgcolor, const nana::rectangle& r, element_state es, const data& crook_data) override
{
if(crook_data.check_state == state::unchecked)
return true;
if(crook_data.radio)
{
unsigned colormap[8][8] = {
{0xFF000000,0xdee7ef,0x737baa,0x232674,0x3c3f84,0x8d96ba,0xe0e9ef,0xFF000000},
{0xdce4ed,0x242875,0x6f71b3,0x9fa0d6,0xc3c4e9,0xb1b2da,0x5c6098,0xdbe4ed},
{0x7b81ad,0x4f5199,0x8182c1,0xa1a2d4,0xccccea,0xcccced,0x9c9dcf,0x7981ae},
{0x2b2d77,0x4f509a,0x696baf,0x7879ba,0xa4a6d4,0xa9aad9,0x9193ce,0x1e2271},
{0x36387f,0x383a87,0x52549c,0x6162a8,0x6f71b3,0x7e7fbf,0x7879ba,0x282c78},
{0x9094ba,0x1b1c71,0x3c3e8b,0x4a4b96,0x585aa1,0x6768ac,0x464893,0x828bb6},
{0xe2eaf1,0x4b4d8d,0x16186d,0x292b7c,0x333584,0x2c2e7f,0x454b8c,0xdfe9f0},
{0xFF000000,0xe4ecf2,0x9599bd,0x454688,0x414386,0x9095bb,0xe3ebf2,0xFF000000}
};
int x = r.x + (static_cast<int>(r.width) - 8) / 2;
int y = r.y + (static_cast<int>(r.height) - 8) / 2;
for(int u = 0; u < 8; ++u)
{
for(int v = 0; v < 8; ++v)
{
if(colormap[u][v] & 0xFF000000)
continue;
graph.set_pixel(x + v, y, colormap[u][v]);
}
++y;
}
}
else
{
int x = r.x + (static_cast<int>(r.width) - 16) / 2;
int y = r.y + (static_cast<int>(r.height) - 16) / 2;
nana::color_t light = graph.mix(fgcolor, 0xFFFFFF, 0.5);
graph.line(x + 3, y + 7, x + 6, y + 10, fgcolor);
graph.line(x + 7, y + 9, x + 12, y + 4, fgcolor);
graph.line(x + 3, y + 8, x + 6, y + 11, light);
graph.line(x + 7, y + 10, x + 12, y + 5, light);
graph.line(x + 4, y + 7, x + 6, y + 9, light);
graph.line(x + 7, y + 8, x + 11, y + 4, light);
}
return true;
}
};
}
template<typename ElementInterface>
class element_object
: nana::noncopyable, nana::nonmovable
{
typedef ElementInterface element_t;
typedef pat::cloneable<element::provider::factory_interface<element_t>> factory_interface;
public:
element_object()
: element_ptr_(nullptr)
{
}
~element_object()
{
if(factory_)
factory_->destroy(element_ptr_);
}
void push(const factory_interface& rhs)
{
auto keep_f = factory_;
auto keep_e = element_ptr_;
factory_ = rhs;
element_ptr_ = factory_->create();
if(nullptr == factory_ || nullptr == element_ptr_)
{
if(element_ptr_)
factory_->destroy(element_ptr_);
factory_.reset();
factory_ = keep_f;
element_ptr_ = keep_e;
}
else
spare_.emplace_back(keep_e, keep_f);
}
element_t * const * keeper() const
{
return &element_ptr_;
}
private:
factory_interface factory_; //Keep the factory for destroying the element
element_t * element_ptr_;
std::vector<std::pair<element_t*, factory_interface>> spare_;
};
class element_manager
: nana::noncopyable, nana::nonmovable
{
//VC2012 does not support alias declaration.
//template<typename E> using factory_interface = element::provider::factory_interface<E>;
template<typename ElementInterface>
struct item
{
element_object<ElementInterface> * employee;
std::map<std::string, std::shared_ptr<element_object<ElementInterface>>> table;
};
element_manager()
{
crook_.employee = nullptr;
}
public:
static element_manager& instance()
{
static bool initial = true;
static element_manager obj;
if(initial)
{
initial = false;
element::add_crook<element::crook>("");
element::add_crook<element::menu_crook>("menu_crook");
}
return obj;
}
void crook(const std::string& name, const pat::cloneable<element::provider::factory_interface<element::crook_interface>>& factory)
{
_m_add(name, crook_, factory);
}
element::crook_interface * const * crook(const std::string& name) const
{
return _m_get(name, crook_).keeper();
}
private:
typedef std::lock_guard<std::recursive_mutex> lock_guard;
template<typename ElementInterface>
void _m_add(const std::string& name, item<ElementInterface>& m, const pat::cloneable<element::provider::factory_interface<ElementInterface>>& factory)
{
typedef element_object<ElementInterface> element_object_t;
lock_guard lock(mutex_);
auto & eop = m.table[name];
if(nullptr == eop)
eop = std::make_shared<element_object_t>();
eop->push(factory);
if(nullptr == m.employee)
m.employee = eop.get();
}
template<typename ElementInterface>
const element_object<ElementInterface>& _m_get(const std::string& name, const item<ElementInterface>& m) const
{
lock_guard lock(mutex_);
auto i = m.table.find(name);
if(i != m.table.end())
return *(i->second);
return *m.employee;
}
private:
mutable std::recursive_mutex mutex_;
item<element::crook_interface> crook_;
};
namespace element
{
//class provider
void provider::add_crook(const std::string& name, const pat::cloneable<factory_interface<crook_interface>>& factory)
{
element_manager::instance().crook(name, factory);
}
crook_interface* const * provider::keeper_crook(const std::string& name)
{
return element_manager::instance().crook(name);
}
}//end namespace element
//facades
//template<> class facade<element::crook>
facade<element::crook>::facade()
: keeper_(element::provider().keeper_crook(""))
{
data_.check_state = state::unchecked;
data_.radio = false;
}
facade<element::crook>::facade(const char* name)
: keeper_(element::provider().keeper_crook(name ? name : ""))
{
data_.check_state = state::unchecked;
data_.radio = false;
}
facade<element::crook> & facade<element::crook>::reverse()
{
data_.check_state = (data_.check_state == facade<element::crook>::state::unchecked ? facade<element::crook>::state::checked : facade<element::crook>::state::unchecked);
return *this;
}
facade<element::crook> & facade<element::crook>::check(state s)
{
data_.check_state = s;
return *this;
}
facade<element::crook>::state facade<element::crook>::checked() const
{
return data_.check_state;
}
facade<element::crook> & facade<element::crook>::radio(bool r)
{
data_.radio = r;
return *this;
}
bool facade<element::crook>::radio() const
{
return data_.radio;
}
void facade<element::crook>::switch_to(const char* name)
{
keeper_ = element::provider().keeper_crook(name);
}
bool facade<element::crook>::draw(graph_reference graph, nana::color_t bgcol, nana::color_t fgcol, const nana::rectangle& r, element_state es)
{
return (*keeper_)->draw(graph, bgcol, fgcol, r, es, data_);
}
//end class facade<element::crook>
namespace element
{
void set_bground(const char* name, const pat::cloneable<element_interface>& obj)
{
detail::bedrock::instance().get_element_store().bground(name, obj);
}
void set_bground(const char* name, pat::cloneable<element_interface> && obj)
{
detail::bedrock::instance().get_element_store().bground(name, std::move(obj));
}
//class cite
cite_bground::cite_bground(const char* name)
: ref_ptr_(detail::bedrock::instance().get_element_store().bground(name))
{
}
void cite_bground::set(const cloneable_element& rhs)
{
holder_ = rhs;
place_ptr_ = holder_.get();
ref_ptr_ = &place_ptr_;
}
void cite_bground::set(const char* name)
{
holder_.reset();
ref_ptr_ = detail::bedrock::instance().get_element_store().bground(name);
}
bool cite_bground::draw(graph_reference dst, nana::color_t bgcolor, nana::color_t fgcolor, const nana::rectangle& r, element_state state)
{
if (ref_ptr_ && *ref_ptr_)
return (*ref_ptr_)->draw(dst, bgcolor, fgcolor, r, state);
return false;
}
//end class cite
//class bground
struct bground::draw_method
{
virtual ~draw_method(){}
virtual draw_method * clone() const = 0;
virtual void paste(const nana::rectangle& from_r, graph_reference, const nana::point& dst_pos) = 0;
virtual void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle & to_r) = 0;
};
struct bground::draw_image
: public draw_method
{
nana::paint::image image;
draw_image(const nana::paint::image& img)
: image(img)
{}
draw_method * clone() const override
{
return new draw_image(image);
}
void paste(const nana::rectangle& from_r, graph_reference dst, const nana::point& dst_pos) override
{
image.paste(from_r, dst, dst_pos);
}
void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle& to_r) override
{
image.stretch(from_r, dst, to_r);
}
};
struct bground::draw_graph
: public draw_method
{
nana::paint::graphics graph;
draw_graph()
{}
draw_graph(const nana::paint::graphics& g)
: graph(g)
{}
draw_method * clone() const override
{
auto p = new draw_graph;
p->graph.make(graph.width(), graph.height());
graph.paste(p->graph, 0, 0);
return p;
}
void paste(const nana::rectangle& from_r, graph_reference dst, const nana::point& dst_pos) override
{
graph.paste(from_r, dst, dst_pos.x, dst_pos.y);
}
void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle& to_r) override
{
graph.stretch(from_r, dst, to_r);
}
};
bground::bground()
: method_(nullptr),
vertical_(false),
stretch_all_(true),
left_(0), top_(0), right_(0), bottom_(0)
{
reset_states();
}
bground::bground(const bground& rhs)
: method_(rhs.method_ ? rhs.method_->clone() : nullptr),
vertical_(rhs.vertical_),
valid_area_(rhs.valid_area_),
states_(rhs.states_),
join_(rhs.join_),
stretch_all_(rhs.stretch_all_),
left_(rhs.left_), top_(rhs.top_), right_(rhs.right_), bottom_(rhs.bottom_)
{
}
bground::~bground()
{
delete method_;
}
bground& bground::operator=(const bground& rhs)
{
if (this != &rhs)
{
delete method_;
method_ = (rhs.method_ ? rhs.method_->clone() : nullptr);
vertical_ = rhs.vertical_;
valid_area_ = rhs.valid_area_;
states_ = rhs.states_;
join_ = rhs.join_;
stretch_all_ = rhs.stretch_all_;
left_ = rhs.left_;
top_ = rhs.top_;
right_ = rhs.right_;
bottom_ = rhs.bottom_;
}
return *this;
}
//Set a picture for the background
bground& bground::image(const paint::image& img, bool vertical, const nana::rectangle& valid_area)
{
delete method_;
method_ = new draw_image(img);
vertical_ = vertical;
if (valid_area.width && valid_area.height)
valid_area_ = valid_area;
else
valid_area_ = nana::rectangle(img.size());
return *this;
}
bground& bground::image(const paint::graphics& graph, bool vertical, const nana::rectangle& valid_area)
{
delete method_;
method_ = new draw_graph(graph);
vertical_ = vertical;
if (valid_area.width && valid_area.height)
valid_area_ = valid_area;
else
valid_area_ = nana::rectangle(graph.size());
return *this;
}
//Set the state sequence of the background picture.
void bground::states(const std::vector<element_state> & s)
{
states_ = s;
}
void bground::states(std::vector<element_state> && s)
{
states_ = std::move(s);
}
void bground::reset_states()
{
states_.clear();
states_.push_back(element_state::normal);
states_.push_back(element_state::hovered);
states_.push_back(element_state::focus_normal);
states_.push_back(element_state::focus_hovered);
states_.push_back(element_state::pressed);
states_.push_back(element_state::disabled);
join_.clear();
}
void bground::join(element_state target, element_state joiner)
{
join_[joiner] = target;
}
void bground::stretch_parts(unsigned left, unsigned top, unsigned right, unsigned bottom)
{
left_ = left;
top_ = top;
right_ = right;
bottom_ = bottom;
stretch_all_ = !(left || right || top || bottom);
}
//Implement the methods of bground_interface.
bool bground::draw(graph_reference dst, nana::color_t bgcolor, nana::color_t fgcolor, const nana::rectangle& to_r, element_state state)
{
if (nullptr == method_)
return false;
auto mi = join_.find(state);
if (mi != join_.end())
state = mi->second;
std::size_t pos = 0;
for (; pos < states_.size(); ++pos)
{
if (states_[pos] == state)
break;
}
if (pos == states_.size())
return false;
nana::rectangle from_r = valid_area_;
if (vertical_)
{
from_r.height /= static_cast<unsigned>(states_.size());
from_r.y += static_cast<int>(from_r.height * pos);
}
else
{
from_r.width /= static_cast<unsigned>(states_.size());
from_r.x += static_cast<int>(from_r.width * pos);
}
if (stretch_all_)
{
if (from_r.width == to_r.width && from_r.height == to_r.height)
method_->paste(from_r, dst, to_r);
else
method_->stretch(from_r, dst, to_r);
return true;
}
auto perf_from_r = from_r;
auto perf_to_r = to_r;
if (left_ + right_ < to_r.width)
{
nana::rectangle src_r = from_r;
src_r.y += static_cast<int>(top_);
src_r.height -= top_ + bottom_;
nana::rectangle dst_r = to_r;
dst_r.y += static_cast<int>(top_);
dst_r.height -= top_ + bottom_;
if (left_)
{
src_r.width = left_;
dst_r.width = left_;
method_->stretch(src_r, dst, dst_r);
perf_from_r.x += static_cast<int>(left_);
perf_from_r.width -= left_;
perf_to_r.x += static_cast<int>(left_);
perf_to_r.width -= left_;
}
if (right_)
{
src_r.x += (static_cast<int>(from_r.width) - static_cast<int>(right_));
src_r.width = right_;
dst_r.x += (static_cast<int>(to_r.width) - static_cast<int>(right_));
dst_r.width = right_;
method_->stretch(src_r, dst, dst_r);
perf_from_r.width -= right_;
perf_to_r.width -= right_;
}
}
if (top_ + bottom_ < to_r.height)
{
nana::rectangle src_r = from_r;
src_r.x += static_cast<int>(left_);
src_r.width -= left_ + right_;
nana::rectangle dst_r = to_r;
dst_r.x += static_cast<int>(left_);
dst_r.width -= left_ + right_;
if (top_)
{
src_r.height = top_;
dst_r.height = top_;
method_->stretch(src_r, dst, dst_r);
perf_from_r.y += static_cast<int>(top_);
perf_to_r.y += static_cast<int>(top_);
}
if (bottom_)
{
src_r.y += static_cast<int>(from_r.height - bottom_);
src_r.height = bottom_;
dst_r.y += static_cast<int>(to_r.height - bottom_);
dst_r.height = bottom_;
method_->stretch(src_r, dst, dst_r);
}
perf_from_r.height -= (top_ + bottom_);
perf_to_r.height -= (top_ + bottom_);
}
if (left_)
{
nana::rectangle src_r = from_r;
src_r.width = left_;
if (top_)
{
src_r.height = top_;
method_->paste(src_r, dst, to_r);
}
if (bottom_)
{
src_r.y += static_cast<int>(from_r.height) - static_cast<int>(bottom_);
src_r.height = bottom_;
method_->paste(src_r, dst, nana::point(to_r.x, to_r.y + static_cast<int>(to_r.height - bottom_)));
}
}
if (right_)
{
const int to_x = to_r.x + int(to_r.width - right_);
nana::rectangle src_r = from_r;
src_r.x += static_cast<int>(src_r.width) - static_cast<int>(right_);
src_r.width = right_;
if (top_)
{
src_r.height = top_;
method_->paste(src_r, dst, nana::point(to_x, to_r.y));
}
if (bottom_)
{
src_r.y += (static_cast<int>(from_r.height) - static_cast<int>(bottom_));
src_r.height = bottom_;
method_->paste(src_r, dst, nana::point(to_x, to_r.y + int(to_r.height - bottom_)));
}
}
method_->stretch(perf_from_r, dst, perf_to_r);
return true;
}
//end class bground
}//end namespace element
}//end namespace nana

1011
source/gui/filebox.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
/*
* Utility Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/layout_utility.hpp
*
*
*/
#include <nana/gui/layout_utility.hpp>
namespace nana
{
//overlap test if overlaped between r1 and r2
bool overlap(const rectangle& r1, const rectangle& r2)
{
if(r1.y + int(r1.height) <= r2.y) return false;
if(r1.y >= int(r2.y + r2.height)) return false;
if(int(r1.x + r1.width) <= r2.x) return false;
if(r1.x >= int(r2.x + r2.width)) return false;
return true;
}
//overlap, compute the overlap area between r1 and r2. the rect is for root
bool overlap(const rectangle& r1, const rectangle& r2, rectangle& r)
{
if(overlap(r1, r2))
{
auto l1 = static_cast<long long>(r1.x) + r1.width;
auto l2 = static_cast<long long>(r2.x) + r2.width;
auto b1 = static_cast<long long>(r1.y) + r1.height;
auto b2 = static_cast<long long>(r2.y) + r2.height;
r.x = r1.x < r2.x ? r2.x : r1.x;
r.y = r1.y < r2.y ? r2.y : r1.y;
r.width = static_cast<unsigned>(l1 < l2 ? l1 - r.x: l2 - r.x);
r.height = static_cast<unsigned>(b1 < b2 ? b1 - r.y: b2 - r.y);
return true;
}
return false;
}
bool overlap(const rectangle& ir, const size& valid_input_area, const rectangle & dr, const size& valid_dst_area, rectangle& op_ir, rectangle& op_dr)
{
if(overlap(ir, valid_input_area, op_ir) == false)
return false;
rectangle good_dr;
if(overlap(dr, valid_dst_area, good_dr) == false)
return false;
zoom(ir, op_ir, dr, op_dr);
if(false == covered(op_dr, good_dr))
{
op_dr = good_dr;
zoom(dr, good_dr, ir, op_ir);
}
return true;
}
bool intersection(const rectangle & r, point pos_beg, point pos_end, point& good_pos_beg, point& good_pos_end)
{
const int right = r.right();
const int bottom = r.bottom();
if(pos_beg.x > pos_end.x)
std::swap(pos_beg, pos_end);
bool good_beg = (0 <= pos_beg.x && pos_beg.x < right && 0 <= pos_beg.y && pos_beg.y < bottom);
bool good_end = (0 <= pos_end.x && pos_end.x < right && 0 <= pos_end.y && pos_end.y < bottom);
if(good_beg && good_end)
{
good_pos_beg = pos_beg;
good_pos_end = pos_end;
return true;
}
else if(pos_beg.x == pos_end.x)
{
if(r.x <= pos_beg.x && pos_beg.x < right)
{
if(pos_beg.y < r.y)
{
if(pos_end.y < r.y)
return false;
good_pos_beg.y = r.y;
good_pos_end.y = (pos_end.y < bottom ? pos_end.y : bottom - 1);
}
else if(pos_beg.y >= bottom)
{
if(pos_end.y >= bottom)
return false;
good_pos_beg.y = bottom - 1;
good_pos_end.y = (pos_end.y < r.y ? r.y : pos_end.y);
}
good_pos_beg.x = good_pos_end.x = r.x;
return true;
}
return false;
}
else if(pos_beg.y == pos_end.y)
{
if(r.y <= pos_beg.y && pos_beg.y < bottom)
{
if(pos_beg.x < r.x)
{
if(pos_end.x < r.x)
return false;
good_pos_beg.x = r.x;
good_pos_end.x = (pos_end.x < right ? pos_end.x : right - 1);
}
else if(pos_beg.x >= right)
{
if(pos_end.x >= right)
return false;
good_pos_beg.x = right - 1;
good_pos_end.x = (pos_end.x < r.x ? r.x : pos_end.x);
}
good_pos_beg.y = good_pos_end.y = r.y;
return true;
}
return false;
}
double m = (pos_end.y - pos_beg.y ) / double(pos_end.x - pos_beg.x);
bool is_nw_to_se = (m >= 0.0);
//The formulas for the line.
//y = m * (x - pos_beg.x) + pos_beg.y
//x = (y - pos_beg.y) / m + pos_beg.x
if(!good_beg)
{
good_pos_beg.y = static_cast<int>(m * (r.x - pos_beg.x)) + pos_beg.y;
if(r.y <= good_pos_beg.y && good_pos_beg.y < bottom)
{
good_pos_beg.x = r.x;
}
else
{
bool cond;
int y;
if(is_nw_to_se)
{
y = r.y;
cond = good_pos_beg.y < y;
}
else
{
y = bottom - 1;
cond = good_pos_beg.y > y;
}
if(cond)
{
good_pos_beg.x = static_cast<int>((y - pos_beg.y) / m) + pos_beg.x;
if(r.x <= good_pos_beg.x && good_pos_beg.x < right)
good_pos_beg.y = y;
else
return false;
}
else
return false;
}
if(good_pos_beg.x < pos_beg.x)
return false;
}
else
good_pos_beg = pos_beg;
if(!good_end)
{
good_pos_end.y = static_cast<int>(m * (right - 1 - pos_beg.x)) + pos_beg.y;
if(r.y <= good_pos_end.y && good_pos_end.y < bottom)
{
good_pos_end.x = right - 1;
}
else
{
bool cond;
int y;
if(is_nw_to_se)
{
y = bottom - 1;
cond = good_pos_end.y > y;
}
else
{
y = r.y;
cond = good_pos_end.y < y;
}
if(cond)
{
good_pos_end.x = static_cast<int>((y - pos_beg.y) / m) + pos_beg.x;
if(r.x <= good_pos_end.x && good_pos_end.x < right)
good_pos_end.y = y;
else
return false;
}
else
return false;
}
if(good_pos_end.x > pos_end.x)
return false;
}
else
good_pos_end = pos_end;
return true;
}
void fit_zoom(const size& input_s, const size& ref_s, size& result_s)
{
double rate_input = double(input_s.width) / double(input_s.height);
double rate_ref = double(ref_s.width) / double(ref_s.height);
if(rate_input < rate_ref)
{
result_s.height = ref_s.height;
result_s.width = static_cast<unsigned>(ref_s.height * rate_input);
}
else if(rate_input > rate_ref)
{
result_s.width = ref_s.width;
result_s.height = static_cast<unsigned>(ref_s.width / rate_input);
}
else
result_s = ref_s;
}
void zoom(const rectangle& ref, const rectangle& scaled, const rectangle& ref_dst, rectangle& r)
{
double rate_x = (scaled.x - ref.x) / double(ref.width);
double rate_y = (scaled.y - ref.y) / double(ref.height);
r.x = static_cast<int>(rate_x * ref_dst.width) + ref_dst.x;
r.y = static_cast<int>(rate_y * ref_dst.height) + ref_dst.y;
r.width = static_cast<unsigned>(scaled.width / double(ref.width) * ref_dst.width);
r.height = static_cast<unsigned>(scaled.height / double(ref.height) * ref_dst.height);
}
//covered test if r2 covers r1.
bool covered(const rectangle& r1, //Rectangle 1 is must under rectangle 2
const rectangle& r2) //Rectangle 2
{
if(r1.x < r2.x || r1.right() > r2.right()) return false;
if(r1.y < r2.y || r1.bottom() > r2.bottom()) return false;
return true;
}
}

464
source/gui/msgbox.cpp Normal file
View File

@@ -0,0 +1,464 @@
/*
* A Message Box Class
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/msgbox.hpp
*/
#include <nana/gui/msgbox.hpp>
#include <nana/gui/programming_interface.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_X11)
#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/label.hpp>
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/picture.hpp>
#include <nana/paint/pixel_buffer.hpp>
#include <nana/gui/place.hpp>
#endif
namespace nana
{
#if defined(NANA_X11)
class msgbox_window
: public form
{
public:
msgbox_window(window wd, const nana::string& title, msgbox::button_t btn, msgbox::icon_t ico)
: form(wd, nana::rectangle(1, 1, 1, 1), appear::decorate<>()),
owner_(wd), pick_(msgbox::pick_yes)
{
this->caption(title);
drawing dw(*this);
dw.draw([this](::nana::paint::graphics& graph)
{
graph.rectangle(nana::rectangle{0, 0, graph.width(), graph.height() - 50}, 0xFFFFFF, true);
if(ico_.empty() == false)
ico_.stretch(ico_.size(), graph, ::nana::rectangle{12, 25, 32, 32});
});
unsigned width_pixel = 45;
unsigned height_pixel = 110;
place_.bind(*this);
yes_.create(*this);
yes_.events().click([this](const arg_mouse& arg)
{
_m_click(arg);
});
yes_.caption(STR("OK"));
width_pixel += 77;
if(msgbox::yes_no == btn || msgbox::yes_no_cancel == btn)
{
yes_.caption(STR("Yes"));
no_.create(*this);
no_.caption(STR("No"));
no_.events().click([this](const arg_mouse& arg)
{
_m_click(arg);
});
width_pixel += 77;
if(msgbox::yes_no_cancel == btn)
{
cancel_.create(*this);
cancel_.caption(STR("Cancel"));
cancel_.events().click([this](const arg_mouse& arg)
{
_m_click(arg);
});
width_pixel += 77;
pick_ = msgbox::pick_cancel;
}
else
pick_ = msgbox::pick_no;
}
std::stringstream ss;
ss<<"vert<><weight=50 margin=[10,0,15,0]<><buttons arrange=70 gap=7 weight="<<(width_pixel - 45)<<">>";
place_.div(ss.str().data());
auto & field = place_.field("buttons");
field<<yes_;
if(!no_.empty())
{
field<<no_;
if(!cancel_.empty())
field<<cancel_;
}
this->size(nana::size{width_pixel, height_pixel});
_m_icon(ico);
}
void prompt(const nana::string& text)
{
if(text.size())
{
const unsigned ico_pixels = (ico_.empty() ? 0 : 44);
const unsigned text_pixels = 500 - ico_pixels;
text_.create(*this, nana::rectangle(12 + ico_pixels, 25, 1, 1));
text_.background(0xFFFFFF);
text_.caption(text);
nana::size ts = text_.measure(text_pixels);
if(ts.height <= 32)
text_.move(12 + ico_pixels, 25 + (32 - ts.height) / 2);
text_.size(ts);
nana::size sz = this->size();
if(sz.width < 48 + ts.width + ico_pixels)
sz.width = 48 + ts.width + ico_pixels;
nana::rectangle r = API::make_center(owner_, sz.width, sz.height + ts.height);
this->move(r);
}
API::modal_window(*this);
}
msgbox::pick_t pick() const
{
return pick_;
}
private:
void _m_icon(msgbox::icon_t ico)
{
if(ico == msgbox::icon_none) return;
const unsigned pic_information[] = {
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x807F7F, 0xA2A2A2, 0xC0C0C0, 0xDADADA, 0xEDEDED, 0xFBFBFB, 0xFBFBFB, 0xEDEDED, 0xDADADA, 0xC0C0C0, 0xA2A2A2, 0x807F7F, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8F8F, 0xBFBFBF, 0xEBEBEB, 0xDADADB, 0xBDBFC4, 0xA9AFBE, 0x99A3BF, 0x8D9ABF, 0x8895B8, 0x8691AC, 0x8F95A6, 0xA8AAB0, 0xD5D6D7, 0xEBEBEB, 0xBFBFBF, 0x8F8F8F, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB9B8B8, 0xEDEDED, 0xC7C8CA, 0xB8BFD2, 0x9CB1E6, 0x7C99E2, 0x7191E1, 0x6C8DE0, 0x6789DE, 0x6487DC, 0x6285D7, 0x6381D0, 0x617EC8, 0x687EB7, 0x7F889F, 0xB9BABE, 0xEDEDED, 0xB9B8B8, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x919090, 0xD3D3D3, 0xDDDEDE, 0xBABFCC, 0x9AB2EE, 0x7494E5, 0x698DE7, 0x668CE8, 0x628AE9, 0x5D85E7, 0x527CE4, 0x4D79E1, 0x527BDF, 0x577DDD, 0x567BD7, 0x577ACF, 0x5878C6, 0x5E77B5, 0x80889B, 0xDADADB, 0xD3D3D3, 0x919090, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x919090, 0xE1E1E1, 0xCACBCB, 0xAFBBD8, 0x7C99E6, 0x688BE5, 0x638AEA, 0x6187E9, 0x5C85E9, 0x5B83E7, 0x5781E7, 0x91ADF2, 0xA5BDF5, 0x688CE6, 0x4A74DA, 0x4F78DA, 0x4D76D6, 0x4D73D1, 0x4F72C7, 0x5472B9, 0x66759A, 0xC1C2C4, 0xE1E1E1, 0x919090, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xCACBCB, 0xA2B2D9, 0x6E8FE0, 0x6287E3, 0x5D85E8, 0x5B84E7, 0x5A83E8, 0x5982E6, 0x527CE3, 0xB8CBF9, 0xFFFFFF, 0xFFFFFF, 0xF4FAFF, 0x6587DF, 0x466FD6, 0x4972D5, 0x4770D1, 0x456DCE, 0x476CC7, 0x4E6DB8, 0x5E709A, 0xC1C2C4, 0xD3D3D3, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB9B8B8, 0xDDDEDE, 0x9FABCB, 0x6D8DDF, 0x5E84E1, 0x5982E5, 0x5981E3, 0x5780E3, 0x5780E3, 0x527CE1, 0x5C83E3, 0xFCFEFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xA7BAE8, 0x3965D0, 0x4770D2, 0x436CD0, 0x4269CC, 0x4068C9, 0x4267C2, 0x4968B4, 0x5E6C91, 0xD9D9DA, 0xB9B8B8, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8F8F, 0xEDEDED, 0x9CA2AE, 0x7493E0, 0x5A80DD, 0x557EE0, 0x547DE1, 0x537DE1, 0x547CE0, 0x537CE0, 0x507ADF, 0x4F78DC, 0xDBE4FB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x7B92CF, 0x3C65D0, 0x456CD0, 0x4169CE, 0x3F67CB, 0x3D64C8, 0x3D63C6, 0x3E62BE, 0x4663AC, 0x757D90, 0xEDEDED, 0x8F8F8F, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xAAA9A9, 0xBFBFBF, 0xBFC0C1, 0x7D95D2, 0x5B7FD8, 0x527ADC, 0x5279DD, 0x5179DC, 0x5079DC, 0x5079DC, 0x5078DC, 0x4F78DB, 0x4771D9, 0x6485DA, 0xCDD5EA, 0xE0E4ED, 0x92A2CB, 0x3C61C1, 0x436AD2, 0x4168CE, 0x3F65CB, 0x3C63C9, 0x3962C7, 0x3960C4, 0x385DC0, 0x3C5EB7, 0x475F9F, 0xB4B6B9, 0xBFBFBF, 0xAAA9A9, 0xFFFFFF,
0xE9E9E9, 0x807F7F, 0xEBEBEB, 0x8C93A5, 0x6284D5, 0x5177D6, 0x4E75D9, 0x4D76DA, 0x4D75D9, 0x4D75D9, 0x4D75D9, 0x4B74D8, 0x4A73D8, 0x4B73D8, 0x3F68D3, 0x3860C9, 0x395EBF, 0x325AC5, 0x4068D2, 0x436AD0, 0x4067CE, 0x3E64CB, 0x3C62C8, 0x395FC6, 0x375EC4, 0x365BC0, 0x365BBB, 0x3E5DAC, 0x667089, 0xEBEBEB, 0x807F7F, 0xE9E9E9,
0xC7C6C6, 0xA2A2A2, 0xD6D6D7, 0x7387BC, 0x5276CF, 0x4A73D5, 0x4972D6, 0x4972D7, 0x4972D7, 0x4972D6, 0x4871D6, 0x4870D5, 0x486FD5, 0x456BD3, 0x4B6FD5, 0x4D72D6, 0x4C71D6, 0x4E72D5, 0x4267CD, 0x3057BF, 0x2E54BC, 0x2950B8, 0x274EB5, 0x264BB4, 0x264DB3, 0x274DB5, 0x2A4FB4, 0x3254AD, 0x435995, 0xD2D2D3, 0xA3A3A3, 0xC7C6C6,
0xA9A8A8, 0xC0C0C0, 0xA6A8AD, 0x5C7AC3, 0x4B72CD, 0x476FD2, 0x466FD2, 0x466FD3, 0x466DD3, 0x466DD3, 0x466DD2, 0x456CD2, 0x436BD3, 0x436BD2, 0xA3B3DD, 0xF5F5F0, 0xEBEAE7, 0xC3CCE6, 0x2C4383, 0xB32A2, 0x1A41A9, 0x1A40A8, 0x1A40A9, 0x1B41A9, 0x1A41A8, 0x1B40A8, 0x1C41A9, 0x2245A5, 0x314D99, 0x9598A0, 0xC1C1C1, 0xA9A8A8,
0x8F8E8E, 0xDADADA, 0x878D9A, 0x5172C3, 0x456CCD, 0x436BCF, 0x426BD0, 0x416BD0, 0x426AD0, 0x426AD0, 0x426AD0, 0x426AD1, 0x3A61CB, 0x3056BE, 0xCFCECE, 0xD0CDCA, 0xC1C1C1, 0xE5E4E3, 0x334984, 0xB31A3, 0x1C42AD, 0x1C41AB, 0x1C41AB, 0x1B41AC, 0x1B41AB, 0x1C41AB, 0x1C41AB, 0x1F43A8, 0x2B499C, 0x6A7184, 0xDADADA, 0x8F8E8E,
0x7C7B7B, 0xEDEDED, 0x737C95, 0x4C6FC3, 0x4169CB, 0x3F68CD, 0x3F67CD, 0x3F67CD, 0x3F67CD, 0x4068CF, 0x3E64CD, 0x2F56BE, 0x1C42AD, 0x2046AF, 0xCACCCC, 0xC5C4C0, 0xB9B9B9, 0xE3E2E1, 0x324784, 0xB31A4, 0x1C42AD, 0x1B40AC, 0x1B40AC, 0x1B40AC, 0x1B40AC, 0x1B40AB, 0x1B40AB, 0x1E41A9, 0x29479F, 0x4C5979, 0xEDEDED, 0x7C7B7B,
0x6F6E6E, 0xFBFBFB, 0x697597, 0x476AC1, 0x3E65C7, 0x3C64C9, 0x3C63CA, 0x3D63CA, 0x3D64CB, 0x355CC5, 0x2349B3, 0x1B41AB, 0x1A40AB, 0x2449B1, 0xCACAC9, 0xBCB9B7, 0xB1B1B1, 0xE1E0DE, 0x324784, 0xB30A5, 0x1B41AE, 0x1B3FAD, 0x1B3FAD, 0x1B3FAD, 0x1B3FAD, 0x1B3FAC, 0x1B3FAC, 0x1D41AA, 0x2645A1, 0x414F7B, 0xFBFBFB, 0x6F6E6E,
0x6F6E6E, 0xFBFBFB, 0x667395, 0x4467BF, 0x3A62C5, 0x3960C7, 0x3961C7, 0x3A61C8, 0x2E56BE, 0x1E45AE, 0x1C42AC, 0x1E43AE, 0x1B41AD, 0x2448B3, 0xC8C8C9, 0xB5B3AF, 0xADADAC, 0xE0DFDE, 0x324784, 0xB31A6, 0x1B40AF, 0x1B3FAE, 0x1B3FAE, 0x1B3FAE, 0x1B3FAE, 0x1B3FAE, 0x1B3FAD, 0x1D40AB, 0x2646A2, 0x404F7B, 0xFBFBFB, 0x6F6E6E,
0x7C7B7B, 0xEDEDED, 0x6B758E, 0x4264B9, 0x385FC2, 0x365EC5, 0x375FC6, 0x2C52BC, 0x1E45AE, 0x1E43AE, 0x1E43AE, 0x1D43AE, 0x1A3FAE, 0x2549B5, 0xC6C7C9, 0xAFAEA9, 0xABABAB, 0xE1E0DE, 0x324784, 0xC30A7, 0x1C40B1, 0x1C3FB1, 0x1C3FB0, 0x1C3FB0, 0x1C3FB0, 0x1B3FAF, 0x1B3FAE, 0x1E41AC, 0x2946A1, 0x4A5678, 0xEDEDED, 0x7C7B7B,
0x8F8E8E, 0xDADADA, 0x7D838F, 0x3E60B3, 0x375DBE, 0x365BC1, 0x2B52BA, 0x1E45AE, 0x1E45AF, 0x1E43AF, 0x1E43AF, 0x1D42AF, 0x1A3FAE, 0x2549B5, 0xC6C7C7, 0xAFAEA9, 0xAEAEAE, 0xE1E1DE, 0x324785, 0xC30A9, 0x1C40B5, 0x1C3FB4, 0x1D3FB4, 0x1D3FB4, 0x1C3FB3, 0x1C3FB3, 0x1D3FB0, 0x2041AD, 0x2C48A0, 0x656C80, 0xDADADA, 0x8F8E8E,
0xA9A8A8, 0xC0C0C0, 0x9EA0A6, 0x425EAA, 0x365BBA, 0x2D53B9, 0x1F46AF, 0x1F46AF, 0x1E43AF, 0x1E43AF, 0x1E42AF, 0x1D42B0, 0x1A3FAF, 0x2449B6, 0xC6C7C9, 0xB4B3AF, 0xB5B5B6, 0xE4E4E1, 0x334786, 0xD30AD, 0x1D41B8, 0x1E3FB7, 0x1D40B7, 0x1D3FB7, 0x1D3FB7, 0x1D3FB6, 0x1E3FB3, 0x2243AE, 0x294398, 0x94979F, 0xC1C1C1, 0xA9A8A8,
0xC7C6C6, 0xA2A2A2, 0xD3D3D4, 0x4D629B, 0x3558B3, 0x224AB0, 0x1F46B0, 0x1F45B0, 0x1E45B0, 0x1E43B0, 0x1D42B0, 0x1D42B1, 0x1A3FB1, 0x2447B7, 0xC8C8C9, 0xBDBCB7, 0xBFBFC0, 0xE8E6E5, 0x334786, 0xD30AF, 0x1E41BC, 0x1F40BA, 0x1E40BA, 0x1E40BA, 0x1E40B9, 0x1E40B8, 0x1F40B6, 0x2845AD, 0x32468B, 0xD2D2D3, 0xA3A3A3, 0xC7C6C6,
0xE9E9E9, 0x807F7F, 0xEBEBEB, 0x6D7489, 0x3251A6, 0x2448AC, 0x2046AF, 0x1F45B0, 0x1E43B0, 0x1E42B1, 0x1E42B1, 0x1D42B1, 0x1B3FB3, 0x2447B9, 0xD0D0CF, 0xC7C6C2, 0xCACBCB, 0xEBEBE9, 0x344887, 0xE30B1, 0x1F41BF, 0x1F40BE, 0x1F40BE, 0x1F40BD, 0x1F40BD, 0x1F40BB, 0x2443B6, 0x2E49A8, 0x5B6380, 0xEBEBEB, 0x818080, 0xE9E9E9,
0xFFFFFF, 0xAAA9A9, 0xBFBFBF, 0xB5B6B8, 0x375092, 0x294BA7, 0x2146AE, 0x2045B0, 0x1E43B1, 0x1E42B1, 0x1E42B1, 0x1D41B3, 0x1B3FB4, 0x2347B9, 0xD5D7D8, 0xD6D5D0, 0xDFDEDF, 0xF2F1EF, 0x344988, 0xE2FB4, 0x2142C1, 0x2041C1, 0x2041C0, 0x2041C0, 0x2140BF, 0x2241BD, 0x2A47B4, 0x304493, 0xB4B5B9, 0xC0C0C0, 0xAAA9A9, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8F8F, 0xEDEDED, 0x6E7484, 0x2F4B9E, 0x2648AB, 0x2046B0, 0x1E42B1, 0x1E42B3, 0x1E42B4, 0x1E41B5, 0x1B3FB6, 0x2346B9, 0xDFDFE3, 0xEBE9E7, 0xF3F3F4, 0xF3F3F2, 0x364988, 0xE2FB5, 0x2342C4, 0x2141C4, 0x2141C4, 0x2141C2, 0x2342C0, 0x2745BA, 0x344CAA, 0x697088, 0xEDEDED, 0x8F8F8F, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB9B8B8, 0xD8D8D9, 0x4C597E, 0x2E4BA1, 0x2548AD, 0x1F43B2, 0x1F42B3, 0x1E42B5, 0x1E41B7, 0x1C3FB8, 0x2143B9, 0xEBEBED, 0xFDFDFA, 0xFCFCFD, 0xF3F3F2, 0x374B8A, 0xE30B7, 0x2443C6, 0x2342C6, 0x2442C5, 0x2542C3, 0x2845BE, 0x334BB0, 0x475484, 0xDBDBDB, 0xBAB9B9, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xBEBEC0, 0x445584, 0x2D4AA2, 0x2546AE, 0x2043B3, 0x2042B5, 0x1F42B8, 0x1D3FBA, 0x1F40B9, 0xB9C0D3, 0xF5F5F6, 0xF5F5F5, 0xC7CCDD, 0x304386, 0x1231B9, 0x2443C8, 0x2442C6, 0x2543C5, 0x2A46BF, 0x344CB3, 0x414F89, 0xC1C2C5, 0xD4D4D4, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x919090, 0xE1E1E1, 0xBFBFC1, 0x4B587F, 0x2E49A0, 0x2848AB, 0x2344B3, 0x2143B7, 0x2142BC, 0x1F40BE, 0x354689, 0x475282, 0x455184, 0x424E83, 0x2942B3, 0x2240C7, 0x2644C7, 0x2945C3, 0x2E48BC, 0x354CB0, 0x4A5587, 0xC3C3C6, 0xE1E1E1, 0x919090, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x919090, 0xD3D3D3, 0xD8D8D9, 0x6C7183, 0x324993, 0x2B47A7, 0x2947B2, 0x2645B8, 0x2444BD, 0x1B3BBC, 0x1635B5, 0x1735B5, 0x1837B8, 0x2442C3, 0x2B48C3, 0x2D48BD, 0x324AB4, 0x33479C, 0x6D728B, 0xDBDCDC, 0xD3D3D3, 0x919090, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB9B8B8, 0xEDEDED, 0xB1B2B5, 0x5E6680, 0x374B92, 0x2E46A3, 0x2D47AE, 0x2C47B4, 0x2C47B9, 0x2D47BB, 0x2E48B8, 0x3049B5, 0x2E46AB, 0x364996, 0x5F6685, 0xB6B8BC, 0xEDEDED, 0xB9B8B8, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8F8F, 0xBFBFBF, 0xEBEBEB, 0xD1D1D2, 0x93969F, 0x666C82, 0x4E587F, 0x434F83, 0x434F83, 0x4E5880, 0x686E86, 0x9698A1, 0xD3D4D5, 0xEBEBEB, 0xBFBFBF, 0x8F8F8F, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x807F7F, 0xA2A2A2, 0xC0C0C0, 0xDADADA, 0xEDEDED, 0xFBFBFB, 0xFBFBFB, 0xEDEDED, 0xDADADA, 0xC0C0C0, 0xA2A2A2, 0x807F7F, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
};
const unsigned pic_warning[] = {
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFDFDFD, 0xD6D7D7, 0xD8D7D8, 0xECECEC, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE6E6E5, 0xB5B5B9, 0xBBBAC3, 0xC8C8C9, 0xFEFEFE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xF3F3F3, 0xAFB0B3, 0xBFBFB7, 0xC2C2AC, 0xB2B2B8, 0xDADADA, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xC9C9C8, 0xADAEBF, 0xB8B26D, 0xC7B81E, 0xBBBBB8, 0xB3B4BB, 0xF7F7F8, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E8, 0xAEAEB5, 0xAEAEAC, 0xD6C611, 0xE4DB06, 0xBCAC4D, 0xB5B8CB, 0xCBCBCB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFDFDFD, 0xC0C0C0, 0xB4B4C2, 0xB6AA53, 0xFFFF00, 0xFFFF00, 0xD1C00E, 0xBDB8A0, 0xB3B4BE, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDBDBDB, 0xB2B2BD, 0xB6B6A6, 0xDDCC07, 0xFFFF00, 0xFFFF00, 0xFFFD00, 0xBBA63E, 0xC3C4D6, 0xC0C0C2, 0xFEFEFE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xF8F8F7, 0xB2B2B5, 0xC3C3D4, 0xBCAC4A, 0xFFFF00, 0xFFF700, 0xFFF700, 0xFFFD00, 0xD2BC0B, 0xCAC39C, 0xB8B9C7, 0xE4E3E4, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCFCFCF, 0xB9B9C7, 0xC1BF97, 0xDBC907, 0x989533, 0x747144, 0x757442, 0x7F7D45, 0xDCCE0D, 0xC4AC31, 0xC6C8D3, 0xBEBEC1, 0xFCFCFC, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEEEEEE, 0xB6B6BB, 0xCDCECE, 0xC6B63D, 0xFAF400, 0x6D6D65, 0x65657E, 0x646578, 0x5E5E74, 0xC4B71F, 0xE4CA0D, 0xC5B679, 0xC5C8D5, 0xD4D4D5, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFEFEFE, 0xC6C6C6, 0xD5D6E2, 0xC7C48C, 0xE0D306, 0xF9F303, 0x76755B, 0x676771, 0x65656A, 0x5D5E62, 0xC6B720, 0xFFEE08, 0xC0A32B, 0xE7E6E8, 0xBBBCC2, 0xF3F3F4, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE4E4E4, 0xBDBEC7, 0xC8C9C5, 0xD7C21D, 0xFFFD01, 0xFCF405, 0x747151, 0x5A5B66, 0x56575B, 0x4D4E4F, 0xC9B71D, 0xFFEA15, 0xE6C60A, 0xC2AB64, 0xE8EAF5, 0xC9C9C9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFCFCFC, 0xB7B7B9, 0xE8E9F2, 0xC3B766, 0xF3E406, 0xFFF512, 0xF8EC0A, 0x706E45, 0x4A4B56, 0x45464B, 0x404041, 0xC9B418, 0xFFE313, 0xF9D708, 0xC3A21B, 0xDAD2BE, 0xC5C8CE, 0xECECEC, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD5D5D5, 0xC2C3CE, 0xC9C9B1, 0xDDC617, 0xFFF00F, 0xFFF019, 0xF8EB0A, 0x6C673A, 0x373844, 0x37383C, 0x3E3D3A, 0xCBB415, 0xFFDD0D, 0xF7D30B, 0xE4B903, 0xBFA449, 0xEDEEF6, 0xC3C3C5, 0xFDFDFD, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xF3F3F3, 0xB6B7BB, 0xE8E9EF, 0xBFAF4F, 0xFFF00D, 0xFFEA1A, 0xFFEC1B, 0xF8E80D, 0x655E2D, 0x292A38, 0x34353B, 0x46453D, 0xD2B711, 0xFFD906, 0xF5CC05, 0xF7CB00, 0xCEA306, 0xC8B68C, 0xCBCDD8, 0xDFDFDF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xC0C0C2, 0xD4D5E4, 0xC7C39D, 0xDFC414, 0xFFEB18, 0xFDE41B, 0xFFE418, 0xF5D711, 0x534A15, 0x80916, 0x1E1F24, 0x3D3B31, 0xCFAD05, 0xFBCB00, 0xF0C300, 0xF0C100, 0xF0B900, 0xB69123, 0xE8EAEE, 0xBDBEC1, 0xF9F9F9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xECECEC, 0xB6B7BD, 0xD8D8D7, 0xC3AD43, 0xFFE506, 0xF8DA0A, 0xF4D302, 0xF7D300, 0xEFC900, 0x453900, 0x0, 0x6070F, 0x3A3829, 0xCDA601, 0xF5BF00, 0xEDB800, 0xEDB400, 0xF0B400, 0xCE9706, 0xC2AB77, 0xCBD0DD, 0xD7D6D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFCFCFC, 0xB8B8BA, 0xD8D9E5, 0xBAAB65, 0xECC902, 0xF4CD00, 0xF0CA00, 0xEEC600, 0xF3C800, 0xF2C600, 0x4D3D00, 0x0, 0x171922, 0x534F3D, 0xD3A901, 0xF4BA00, 0xEDB200, 0xEDB100, 0xECB000, 0xEDAE00, 0xB88B16, 0xCBC5BB, 0xBABCC0, 0xF3F3F3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xDEDEDE, 0xB4B5BF, 0xB8B8AC, 0xD1AE0D, 0xF6CD00, 0xEFC900, 0xEFC600, 0xEEC400, 0xF2C500, 0xF6C900, 0x5D4A00, 0x4, 0x202435, 0x5A5544, 0xD4A801, 0xF4B800, 0xEDB000, 0xECAF00, 0xEBAC00, 0xECAC00, 0xD99903, 0xB5964E, 0xC6CBDA, 0xC7C7C7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xF8F8F8, 0xB2B3B5, 0xC4C5D1, 0xB9A550, 0xEAC102, 0xEFC800, 0xEEC500, 0xEEC400, 0xEEC200, 0xEEC100, 0xF0C000, 0xD1A100, 0xB38800, 0xBB9100, 0xCA9B00, 0xE5AB00, 0xEDB100, 0xEBAE00, 0xEAAC00, 0xEAA900, 0xE8A700, 0xEDA800, 0xC28B06, 0xB9AB8D, 0xB7BBC3, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xC7C7C8, 0xB9BCCB, 0xB5B090, 0xD5AE06, 0xF0C800, 0xEEC300, 0xEEC200, 0xEEC100, 0xEDBD00, 0xEDBB00, 0xF2BB00, 0xCD9E00, 0x907000, 0x806506, 0xB68A00, 0xF4B400, 0xEBAB00, 0xEAA900, 0xEAA800, 0xE7A600, 0xE7A400, 0xE7A300, 0xDC9803, 0xB18C36, 0xC2C5D3, 0xBFBFC0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xF1F1F1, 0xAFB0B6, 0xB7B7B8, 0xBBA543, 0xEBBF02, 0xEEC400, 0xEEC100, 0xEEBF00, 0xEEBD00, 0xEDBA00, 0xEFBA00, 0xF4BD00, 0x765A00, 0x5, 0x161B33, 0x645A38, 0xDBA200, 0xEEAC00, 0xEAA800, 0xE8A700, 0xE7A400, 0xE7A400, 0xE7A400, 0xEBA600, 0xC58B06, 0xB19E75, 0xB6BBC5, 0xDEDEDE, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xBCBCBD, 0xB4B7C4, 0xB3AB77, 0xD8B006, 0xF3C805, 0xF0C506, 0xF0C305, 0xF0C102, 0xEFC001, 0xF0BC00, 0xF4BD00, 0xF5C000, 0x554200, 0xA, 0x343944, 0x5F5D57, 0xD19C06, 0xF3B300, 0xEBAE04, 0xEBAC05, 0xEAAB06, 0xEAAB06, 0xEBAB05, 0xECAC06, 0xE1A203, 0xB38823, 0xBCBBBF, 0xB5B7BB, 0xF8F8F8, 0xFFFFFF,
0xFFFFFF, 0xE8E8E7, 0xAEAEB3, 0xB1AFA0, 0xC3AB45, 0xFED923, 0xF8D329, 0xF8D028, 0xF8CE27, 0xF8CD26, 0xF7CC24, 0xF8CB23, 0xF8CA22, 0xFFCE20, 0xC9A21E, 0x4C4324, 0x55544A, 0x9D863E, 0xECB821, 0xF7C228, 0xF3BD29, 0xF2BD2A, 0xF3BF2A, 0xF3BF29, 0xF3BF29, 0xF4BF29, 0xF9C227, 0xD19F19, 0xB09964, 0xB2B4BF, 0xD1D1D2, 0xFFFFFF,
0xFFFFFF, 0xBABABB, 0xB7B8C0, 0xB6AA73, 0xCCA420, 0xCBA326, 0xCBA126, 0xCB9F26, 0xCB9E24, 0xCB9E24, 0xCB9E24, 0xCB9E23, 0xCA9E22, 0xCA9E22, 0xCE9E22, 0xC49822, 0xBB8F1F, 0xC59820, 0xC99B23, 0xC89B26, 0xC89A26, 0xC89A26, 0xC89B26, 0xC99B26, 0xC99B26, 0xC99B26, 0xCA9B26, 0xCC9E22, 0xBC9637, 0xBAB4B0, 0xB1B3B7, 0xEFEFF0,
0xFFFFFF, 0xB2B3B4, 0xC9CACC, 0xD2CFC9, 0xD3CFCA, 0xD3CFCB, 0xD4CFCD, 0xD5D1CE, 0xD6D2CF, 0xDDDAD6, 0xE3DFDD, 0xEAE7E6, 0xEEEBEA, 0xEFEDEA, 0xEFEDEB, 0xF0EDEB, 0xF0EEEB, 0xEFEDEB, 0xEFEDEB, 0xEDEAE7, 0xE7E5E3, 0xE7E4E3, 0xDFDAD8, 0xD8D4D1, 0xD6D2CF, 0xD5D1CE, 0xD4CFCD, 0xD3CECB, 0xD4CFCD, 0xD0CECF, 0xBDBDC0, 0xC9C9C9,
0xFFFFFF, 0xBBBBBB, 0x9F9FA0, 0x979799, 0x97979B, 0x97979B, 0x97989B, 0x98999C, 0x999B9C, 0x9B9C9E, 0x9C9CA1, 0x9C9DA1, 0x9DA1A2, 0xA1A1A3, 0xA1A1A3, 0xA1A2A4, 0xA2A2A4, 0xA1A2A4, 0xA1A1A3, 0xA1A1A3, 0x9EA1A2, 0x9D9EA1, 0x9D9DA0, 0x9C9D9E, 0x9A9B9D, 0x999A9D, 0x98999C, 0x97989B, 0x98989B, 0x989899, 0xA7A7A7, 0xE0E0E0,
0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xD1D1D1, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
};
const unsigned pic_error[] = {
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x807F7F, 0xA2A2A2, 0xC0C0C0, 0xDADADA, 0xEEEDED, 0xFBFBFB, 0xFBFBFB, 0xEEEDED, 0xDADADA, 0xC0C0C0, 0xA2A2A2, 0x807F7F, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8F8F, 0xBFBFBF, 0xECEBEB, 0xEBD9D8, 0xE4BDBD, 0xDCA7A8, 0xD79596, 0xD48688, 0xCE7D7F, 0xD28B8C, 0xD9A2A1, 0xE1BEC0, 0xEAD9D9, 0xECEAEA, 0xBFBFBF, 0x8F8F8F, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB9B8B8, 0xEFECEC, 0xE5C2C3, 0xE1AEAD, 0xE89D9E, 0xE06F6F, 0xE15C5C, 0xE25254, 0xE15150, 0xDF4F4F, 0xDB4F4F, 0xD65051, 0xCF5151, 0xC85F5F, 0xD3A1A1, 0xE0C8C7, 0xE7E4E4, 0xB9B8B8, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x919090, 0xD3D3D3, 0xECD0D0, 0xDFA9AA, 0xECA2A2, 0xE36565, 0xE65151, 0xE44F4F, 0xE44D4F, 0xE04D4D, 0xDF4D4D, 0xDC4D4D, 0xDB494C, 0xD84A4A, 0xD64947, 0xD44747, 0xCB4949, 0xC34B4B, 0xD08E8E, 0xE8CCCD, 0xD3D3D3, 0x919090, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x919090, 0xE1E1E1, 0xE2B5B5, 0xE4A6A7, 0xE57778, 0xE55151, 0xE44F4F, 0xE24E4E, 0xE04D4D, 0xDE4C4C, 0xDC4B4B, 0xDB4949, 0xD74848, 0xD54747, 0xD34545, 0xD14545, 0xCF4343, 0xCC4242, 0xCB3F3F, 0xC24343, 0xB64546, 0xDCADAD, 0xE1E1E1, 0x919090, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xE2B5B5, 0xE49E9E, 0xE26667, 0xE6504E, 0xE04646, 0xDA3D3D, 0xDC4444, 0xDC4B4B, 0xDB4848, 0xD94748, 0xD54646, 0xD34344, 0xD14343, 0xCF4242, 0xCD4341, 0xC83A3A, 0xC52F31, 0xC33535, 0xC33C3C, 0xBE3D3D, 0xB53F40, 0xDCACAC, 0xD3D3D3, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB9B8B8, 0xECD0D0, 0xDB8F8F, 0xE36868, 0xE24E4E, 0xDB403E, 0xE96565, 0xF7B2B2, 0xE46565, 0xD23B39, 0xD74646, 0xD44343, 0xD14343, 0xCF4242, 0xCB4040, 0xCA3F3F, 0xC43333, 0xCB4E4E, 0xE29E9E, 0xCF5B5C, 0xBA2A29, 0xBC3636, 0xB83839, 0xB13E3F, 0xE9CBCB, 0xB9B8B8, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8F8F, 0xEFECEC, 0xD69696, 0xE37B7B, 0xE04B4D, 0xDB4141, 0xE65F5F, 0xFFE7E7, 0xFFFFFF, 0xFFE9E9, 0xDC5A5A, 0xCA3333, 0xCF4242, 0xCB4040, 0xC93D3D, 0xC83E3D, 0xC23030, 0xC94848, 0xDDC0C0, 0xDEEEEC, 0xE0D0D0, 0xC75455, 0xB32828, 0xB43232, 0xB13434, 0xB75354, 0xEFECEC, 0x8F8F8F, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xAAA9A9, 0xBEBEBE, 0xE6CAC9, 0xDB797A, 0xDF4C4C, 0xDB4141, 0xE05757, 0xFFEAEA, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFE7E8, 0xD75B5B, 0xC62E2E, 0xC93E3E, 0xC53A3A, 0xC12E2C, 0xC8494A, 0xDDC2C2, 0xDAE3E3, 0xD4D5D5, 0xD3DADA, 0xD9CBCA, 0xBB4747, 0xAD2525, 0xAC2B2C, 0xAE3233, 0xD8A4A5, 0xBFBFBF, 0xAAA9A9, 0xFFFFFF,
0xE9E9E9, 0x807F7F, 0xEBE9E9, 0xDAA7A7, 0xDB6060, 0xDB4745, 0xD63C3C, 0xDE5758, 0xFFF2F2, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xF8E8E8, 0xD45B5B, 0xBD2828, 0xBD2B2A, 0xC54949, 0xDBC3C3, 0xDAE4E4, 0xD5D5D5, 0xD0CECE, 0xE5E8E8, 0xF4F4F4, 0xAF4949, 0xA62121, 0xA62A2A, 0xA92B2C, 0xB85755, 0xECEAEA, 0x807F7F, 0xE9E9E9,
0xC7C6C6, 0xA2A2A2, 0xEAD9DA, 0xCF6B6B, 0xD74444, 0xD64341, 0xD34242, 0xCD3434, 0xDB6464, 0xFFEFEF, 0xFFFFFF, 0xFCFCFC, 0xF6F6F6, 0xF4FCFC, 0xF0E1E2, 0xCC5050, 0xC14040, 0xDBC3C3, 0xD8E1E1, 0xD5D4D4, 0xCFCFCF, 0xE5E8E8, 0xF4F2F2, 0xA24040, 0x990C0C, 0xA20F0F, 0xA00F0F, 0x9D0F0F, 0xA92F30, 0xE8D1D1, 0xA3A3A3, 0xC7C6C6,
0xA9A8A8, 0xC0C0C0, 0xE1BABD, 0xCF5556, 0xD44141, 0xD23F3F, 0xCE3F3F, 0xCC3D3D, 0xC32A2C, 0xD35E5E, 0xF6EBEB, 0xFAFFFF, 0xF1F1F1, 0xEEEDED, 0xE9F0F0, 0xE6D2D2, 0xD6BDBD, 0xD3DADA, 0xCFCFCF, 0xCAC9C9, 0xE2E5E5, 0xF3F1F1, 0xA03A3A, 0x980C0C, 0xA30B0B, 0x9F0B0B, 0x9E0B0B, 0x9E0B0B, 0xA41C1C, 0xD39C9C, 0xC1C1C1, 0xA9A8A8,
0x8F8E8E, 0xDADADA, 0xD59B9C, 0xCD4949, 0xD03D3E, 0xCE3D3C, 0xCA3C3C, 0xC83A3A, 0xC7393B, 0xBD2828, 0xCC5C5C, 0xEDE5E5, 0xEDF4F4, 0xE6E5E5, 0xDEDEDE, 0xD9DCDC, 0xD3D9D9, 0xCDCDCD, 0xC8C8C8, 0xE1E5E5, 0xF3F1F1, 0xA03F3F, 0x990C0C, 0xA40A0A, 0xA10B0B, 0xA00B0B, 0xA00B0B, 0x9F0B0B, 0xA21313, 0xC06B6B, 0xDADADA, 0x8F8E8E,
0x7C7B7B, 0xEEEDED, 0xCB807F, 0xCC4140, 0xCA3C3C, 0xC83A3A, 0xC83A38, 0xC43838, 0xC23636, 0xC03939, 0xB72321, 0xC24A4A, 0xDECBCB, 0xDCE0E0, 0xD6D6D6, 0xD3D2D2, 0xCECDCD, 0xC9C9C9, 0xE1E2E2, 0xF2F1F1, 0xA34242, 0x990C0C, 0xA40A0A, 0xA40A0A, 0xA40A0A, 0xA30B0B, 0xA30B0B, 0xA10B0B, 0xA10E0E, 0xB04344, 0xEEEDED, 0x7C7B7B,
0x6F6E6E, 0xFBFBFB, 0xBE5959, 0xCA3B3B, 0xC83A3A, 0xC43737, 0xC23535, 0xC03636, 0xBE3636, 0xB82323, 0xB10909, 0xA70A0A, 0xBE4949, 0xD4D6D6, 0xD1D3D3, 0xCDCDCD, 0xC8C8C8, 0xC3C4C4, 0xEDEDED, 0xB35F5F, 0x980C0C, 0xA70A0A, 0xA60A0A, 0xA60A0A, 0xA60A0A, 0xA40A0A, 0xA60A0A, 0xA40A0A, 0xA40B0B, 0xA62D2D, 0xFBFBFB, 0x6F6E6E,
0x6F6E6E, 0xF9F9F9, 0xC16666, 0xC43838, 0xC23535, 0xC03434, 0xBE3535, 0xBC3030, 0xB41313, 0xAD0909, 0xA80A0A, 0xB31E1E, 0xD0AAAA, 0xCDD3D3, 0xCCCCCC, 0xC8C8C8, 0xC3C3C3, 0xC1C2C2, 0xBFC4C4, 0xCBB2B2, 0xB42B2B, 0xA40A0A, 0xA80A0A, 0xA80A0A, 0xA90A0A, 0xA90A0A, 0xA90A0A, 0xA90A0A, 0xA90B0B, 0xA63131, 0xFAFAFA, 0x6F6E6E,
0x7C7B7B, 0xEBEAEB, 0xC77C7D, 0xBF3838, 0xBE3434, 0xBE3635, 0xB82A2A, 0xB00909, 0xAC0909, 0xA80A0A, 0xB11C1C, 0xD0B2B2, 0xCCD7D7, 0xCBCBCB, 0xC8C7C7, 0xC3C8C8, 0xC3C6C6, 0xC1BFBF, 0xBDBDBD, 0xBCC5C5, 0xCEB8B8, 0xB52929, 0xA80A0A, 0xAC0909, 0xAD0909, 0xAF0909, 0xAF0909, 0xAF0909, 0xB00C0C, 0xAF4747, 0xEDECEC, 0x7C7B7B,
0x8F8E8E, 0xD7D8D9, 0xD19999, 0xBB3838, 0xBC3636, 0xB72C2C, 0xAD0909, 0xA90A0A, 0xA40A0A, 0xAF1C1C, 0xCFB1B1, 0xCBD6D6, 0xCBCCCC, 0xC7C7C7, 0xE1E4E4, 0xEEECEC, 0xB7ACAC, 0xBCC2C2, 0xBFBEBE, 0xC0C0C0, 0xC6CFCF, 0xD5C1C1, 0xB82727, 0xAC0909, 0xB20909, 0xB20909, 0xB40909, 0xB40808, 0xB50E0E, 0xBF6E6E, 0xD9D9D9, 0x8F8E8E,
0xA9A8A8, 0xBEBEBE, 0xDCBBBA, 0xB7393A, 0xB82F2F, 0xAD0909, 0xA90A0A, 0xA60A0A, 0xAC1515, 0xCFADAD, 0xCBD6D6, 0xCACBCB, 0xC6C6C6, 0xE1E4E4, 0xEFEEEE, 0xA13838, 0xA22222, 0xB8ABAC, 0xC0C8C8, 0xC8C7C7, 0xCDCDCD, 0xD9E1E1, 0xE1CAC8, 0xBC2424, 0xB40808, 0xB90808, 0xBA0808, 0xBB0808, 0xAB0E0F, 0xD5A2A1, 0xC0C0C0, 0xA9A8A8,
0xC7C6C6, 0xA0A1A0, 0xE4D7D6, 0xB33A3A, 0xAD1212, 0xA80A0A, 0xA40A0A, 0xAA1313, 0xCFABAB, 0xCBD6D6, 0xCACACA, 0xC6C6C6, 0xE0E4E4, 0xEFEEEE, 0xA03F3F, 0x990C0C, 0xA60A0A, 0xAB2828, 0xBFB2B2, 0xCED8D8, 0xD8D6D6, 0xE0E0E0, 0xEDF5F6, 0xEDD1D1, 0xC01C1E, 0xBE0707, 0xBF0707, 0xC00707, 0xAA2021, 0xE9D5D3, 0xA3A3A2, 0xC7C6C6,
0xE9E9E9, 0x807F7F, 0xE5E3E3, 0xB24646, 0xA81414, 0xA40A0A, 0xA00B0B, 0xA92121, 0xCABDBD, 0xC8D0D0, 0xC5C5C5, 0xE1E3E3, 0xEFEDED, 0x9E3E3E, 0x980C0C, 0xA60A0A, 0xA80A0A, 0xA90A0A, 0xB02B2B, 0xCDC0C0, 0xE2EAEA, 0xEBEBEB, 0xF8FEFE, 0xEEEDED, 0xBD2828, 0xC40707, 0xC70908, 0xC40F0F, 0xCB8887, 0xECEBEB, 0x818080, 0xE9E9E9,
0xFFFFFF, 0xAAA9A9, 0xBFBFBF, 0xD59B9B, 0xA61C1C, 0xA10C0C, 0x9F0B0B, 0x9A0B0B, 0xA73535, 0xBEB5B5, 0xDFE6E6, 0xEFEDED, 0x9C3C3C, 0x970C0C, 0xA40A0A, 0xA60A0A, 0xA90A0A, 0xAD0909, 0xB00909, 0xB52626, 0xDECECE, 0xFBFFFF, 0xF1EEEE, 0xBA4848, 0xBC0808, 0xCD0A0A, 0xCE0B0B, 0xAB1111, 0xE0C0BE, 0xBFC0BF, 0xAAA9A9, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8F8F, 0xEFECEC, 0xB14545, 0xA41616, 0x9B0B0B, 0x990C0C, 0x960C0C, 0xA23333, 0xD0B9B9, 0x9B3A39, 0x950C0C, 0xA10B0B, 0xA40A0A, 0xA70A0A, 0xAB0A0A, 0xB00909, 0xB40808, 0xB70808, 0xC22F2F, 0xE2AEAE, 0xBF4B4B, 0xBE0707, 0xD10B0B, 0xD30C0C, 0xCC1314, 0xB14848, 0xEFECEC, 0x8F8F8F, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB9B8B8, 0xE8CACA, 0xA32425, 0x9F1313, 0x970C0C, 0x950C0C, 0x950C0C, 0x910C0C, 0x950C0C, 0x9E0B0B, 0xA00B0B, 0xA40A0A, 0xA80A0A, 0xAD0909, 0xB20909, 0xB80808, 0xBC0808, 0xC00707, 0xBC0808, 0xC50707, 0xD30C0C, 0xD70D0D, 0xD11212, 0xA72020, 0xEBCDCD, 0xBAB9B9, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xD9A8A8, 0xA42020, 0x9B1313, 0x920C0C, 0x950C0C, 0x970C0C, 0x990C0C, 0x9E0B0B, 0xA00B0B, 0xA40A0A, 0xA90A0A, 0xB00909, 0xB40808, 0xBB0808, 0xC00707, 0xC60A0A, 0xCC0909, 0xD30C0C, 0xD80D0D, 0xD31313, 0xA81A1A, 0xDEADAE, 0xD4D4D4, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x919090, 0xE1E1E1, 0xDAA8A8, 0xA02323, 0x9C1515, 0x920D0D, 0x950C0C, 0x990C0C, 0x9E0B0B, 0xA00B0B, 0xA60A0A, 0xAC0909, 0xB20909, 0xB80808, 0xBC0808, 0xC30808, 0xC90C0C, 0xD00C0C, 0xD60D0D, 0xCF1313, 0xA92222, 0xDEAFAF, 0xE1E1E1, 0x919090, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x919090, 0xD3D3D3, 0xE8CACA, 0xB04344, 0x991717, 0x9C1111, 0x980C0C, 0x9B0B0B, 0xA00B0B, 0xA60A0A, 0xAC0909, 0xB20909, 0xBA0808, 0xBF0707, 0xC8090B, 0xCE0D0D, 0xCC1111, 0xAF1010, 0xB2494A, 0xEBCFCF, 0xD3D3D3, 0x919090, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB9B8B8, 0xEFECEC, 0xDFC0C0, 0xC47779, 0xA02122, 0x9B1212, 0xA41010, 0xA80C0C, 0xAC0A0A, 0xB40A0A, 0xB90A0A, 0xBE0D0D, 0xB10F0F, 0xA61111, 0xB85656, 0xDCADAE, 0xEFECEC, 0xB9B8B8, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8F8F, 0xBDBDBD, 0xEBE9E9, 0xE9D9D9, 0xDDB5B5, 0xCD8B8B, 0xB75A59, 0xA83937, 0xA42C2B, 0xB1494A, 0xC17171, 0xD7A2A1, 0xE8D3D3, 0xEBEAEA, 0xBFBEBE, 0x8F8F8F, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x807F7F, 0xA2A2A2, 0xC0C0C0, 0xDADADA, 0xEEEDED, 0xFBFBFB, 0xFBFBFB, 0xEEEDED, 0xDADADA, 0xC0C0C0, 0xA2A2A2, 0x807F7F, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
};
const unsigned pic_question[] = {
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x7F7E7E, 0xA1A1A1, 0xC0C0C0, 0xDADADA, 0xEDEDED, 0xFBFBFB, 0xFBFBFB, 0xEDEDED, 0xDADADA, 0xC0C0C0, 0xA1A1A1, 0x7F7E7E, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8E8E, 0xBFBFBF, 0xEBEBEB, 0xDADADA, 0xBFC0C3, 0xAFB4C4, 0x9AA7CA, 0x7D8EC0, 0x7889BE, 0x8898C5, 0xA1AAC5, 0xB4B6C0, 0xDAD9DA, 0xEBEBEB, 0xBFBFBF, 0x8F8E8E, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB8B7B7, 0xEDEDED, 0xCACACA, 0xBEC2CF, 0x9BADDF, 0x698CE8, 0x4C75E3, 0x406BE0, 0x416DE0, 0x3F6BDD, 0x3B67D8, 0x3864D4, 0x3E68D1, 0x5375CC, 0x8A98BF, 0xC4C5CA, 0xEDEDED, 0xB8B7B7, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x908F8F, 0xD3D3D3, 0xDEDEDE, 0xC3C6CE, 0xA1B5E9, 0x6B8FED, 0x4C77E7, 0x446FE0, 0x446EDD, 0x456EDB, 0x426DD9, 0x416BD6, 0x4069D4, 0x3D67D1, 0x3C65CE, 0x3761CB, 0x305AC6, 0x3B60C3, 0x8694B8, 0xDDDDDD, 0xD3D3D3, 0x908F8F, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x908F8F, 0xE1E1E1, 0xCECECE, 0xBFC8DF, 0x819EEF, 0x537CE8, 0x416DE0, 0x446EDE, 0x456EDB, 0x416AD9, 0x3D67D6, 0x3864D3, 0x3761CF, 0x3763CD, 0x3A63CB, 0x3C64CA, 0x3B63C7, 0x3860C4, 0x335DC1, 0x2852BB, 0x5570B3, 0xC7C8CB, 0xE1E1E1, 0x908F8F, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xCECECE, 0xBAC7E9, 0x7092ED, 0x4671E2, 0x426DDE, 0x466FDD, 0x3F69D8, 0x3865D6, 0x4870D8, 0x5F82DB, 0x7290DF, 0x7290DD, 0x5E7FD6, 0x446ACB, 0x3059C2, 0x335CC1, 0x365EC1, 0x345CBC, 0x345BBA, 0x2951B5, 0x3E5EAE, 0xC5C6CB, 0xD3D3D3, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB8B7B7, 0xDEDEDE, 0xB7C0DA, 0x6E91EC, 0x426DDF, 0x456EDD, 0x456EDB, 0x3F6AD8, 0x567CDD, 0xAEC1F2, 0xEBF1FF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFAFCFF, 0xE7EAF8, 0xA3B5E3, 0x4468C2, 0x2C55B9, 0x335AB8, 0x3157B5, 0x3056B4, 0x244CAE, 0x405DA4, 0xDBDBDC, 0xB8B7B7, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8E8E, 0xEDEDED, 0xAEB1BA, 0x7A9AED, 0x456FDF, 0x446DDB, 0x456ED9, 0x426BD7, 0x3763D1, 0xB1C3F1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFEFD, 0xFFFFFC, 0xD4DAED, 0x4466BA, 0x2B52B2, 0x2E55B0, 0x2C53AF, 0x2B51AD, 0x1F47A5, 0x6576A1, 0xEDEDED, 0x8F8E8E, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xAAA9A9, 0xBFBFBF, 0xC3C3C3, 0x92A7E1, 0x537AE2, 0x406AD9, 0x446DD8, 0x426BD6, 0x3F68D3, 0x3661CE, 0xB6C5EE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFCFCFE, 0xF8F8FA, 0xFFFFFD, 0xAAB8D9, 0x264CAC, 0x2C52AE, 0x2C51AC, 0x2B50A8, 0x2A4FA6, 0x1E429C, 0xB1B5BF, 0xBFBFBF, 0xAAA9A9, 0xFFFFFF,
0xE9E9E9, 0x7F7E7E, 0xEBEBEB, 0x9DA3B4, 0x6B8DE8, 0x3E69D7, 0x416AD7, 0x4069D4, 0x3F68D0, 0x3D67CE, 0x345ECA, 0xB7C4EA, 0xFFFFFA, 0xC2C9DA, 0x8B9BC5, 0x778DC2, 0x99AAD1, 0xF2F3F8, 0xFEFDFF, 0xFAFAFD, 0xFAFAFC, 0xE8EAF2, 0x3859AA, 0x2A50AB, 0x2C51A8, 0x2C50A6, 0x2A4FA4, 0x244AA0, 0x506698, 0xEBEBEB, 0x7F7E7E, 0xE9E9E9,
0xC7C6C6, 0xA1A1A1, 0xD6D6D6, 0x8498CE, 0x4E75DD, 0x3D67D4, 0x3F68D3, 0x3E66CF, 0x3D66CC, 0x3C64CA, 0x365FC6, 0x7990CF, 0x7085B8, 0x2D52AE, 0x2750B4, 0x2650B4, 0x1F48AD, 0x8499CE, 0xFDFCFA, 0xF8F8FA, 0xF9F8FC, 0xF5F6F7, 0x4865A8, 0x284FA6, 0x2C51A5, 0x2C50A3, 0x2B4FA2, 0x2B4EA1, 0x244797, 0xCBCDD2, 0xA2A2A2, 0xC7C6C6,
0xA9A8A8, 0xC0C0C0, 0xA8AAB0, 0x5E82DA, 0x3E68D4, 0x3D67CF, 0x3E66CD, 0x3D65CB, 0x3B63C9, 0x3A61C5, 0x385FC3, 0x3057BE, 0x2B54B9, 0x335CBC, 0x345BB9, 0x345AB7, 0x274FB0, 0x5B78BF, 0xE9E9EB, 0xEDECEF, 0xF2F1F4, 0xEBEBF1, 0x31509B, 0x19419D, 0x1D429C, 0x1E429B, 0x1E4299, 0x204499, 0x173D96, 0x7F8AA3, 0xC1C1C1, 0xA9A8A8,
0x8F8E8E, 0xDADADA, 0x8E94A5, 0x476ED5, 0x3B64CD, 0x3D66CC, 0x3C64C9, 0x3B61C6, 0x3860C4, 0x365EC2, 0x365DBF, 0x355CBB, 0x345BBA, 0x335AB7, 0x2E54B4, 0x244CAD, 0x133EA5, 0x7587B8, 0xD3D0D1, 0xD8D8DE, 0xEFEEEF, 0xBBC2D5, 0x173B91, 0x193F9A, 0x1C4199, 0x1D4199, 0x1D4198, 0x1D4197, 0x1C4096, 0x44598B, 0xDADADA, 0x8F8E8E,
0x7C7B7B, 0xEDEDED, 0x7683A3, 0x3E67D0, 0x3B63CA, 0x3B63C7, 0x3860C4, 0x375FC2, 0x365DC0, 0x355CBE, 0x355CBA, 0x3359B7, 0x2B52B2, 0x2149AC, 0x1640A5, 0xC37A1, 0x4E68AB, 0xB5B4BA, 0xC0BFC3, 0xD1D0D6, 0xEDECEC, 0x5C72A4, 0x123893, 0x1E4299, 0x1E4299, 0x1E4298, 0x1F4298, 0x204297, 0x214498, 0x2C478A, 0xEDEDED, 0x7C7B7B,
0x6F6E6E, 0xFBFBFB, 0x63749F, 0x3B64CC, 0x375FC4, 0x365EC3, 0x355DC0, 0x345CBE, 0x345BBB, 0x3359B7, 0x2A52B2, 0x2048AB, 0x1740A6, 0x143DA2, 0x143DA1, 0x5E74AC, 0xB1B1B7, 0xB2B1B6, 0xBABABF, 0xD6D4D6, 0x8592B5, 0x183C90, 0x20459C, 0x22469A, 0x22459A, 0x224599, 0x234699, 0x22459A, 0x23469A, 0x2B4990, 0xFBFBFB, 0x6F6E6E,
0x6F6E6E, 0xFBFBFB, 0x617098, 0x3660C7, 0x355DBF, 0x345CBE, 0x335BBB, 0x345AB8, 0x2E54B4, 0x1F47AB, 0x1640A6, 0x143EA3, 0x153EA1, 0xE389D, 0x5F75AD, 0xC0BCBC, 0xB4B4B8, 0xB6B6BB, 0xC1C1C4, 0x6577A2, 0x1C3E92, 0x274A9E, 0x2A4B9E, 0x2A4C9E, 0x2A4C9E, 0x2A4C9E, 0x2A4BA0, 0x294BA0, 0x294A9E, 0x2F4B94, 0xFBFBFB, 0x6F6E6E,
0x7C7B7B, 0xEDEDED, 0x6B7694, 0x335BC0, 0x335ABB, 0x3259B8, 0x3257B6, 0x2951B0, 0x1944A7, 0x153FA4, 0x153EA2, 0x143DA0, 0x133B9C, 0x1F45A0, 0xB1B6C4, 0xC4C3C6, 0xBFBFC4, 0xBEBEC4, 0x546798, 0x1D4096, 0x2C4FA2, 0x3051A2, 0x3151A2, 0x3252A3, 0x3252A4, 0x3252A5, 0x3152A5, 0x3151A5, 0x3050A5, 0x354F93, 0xEDEDED, 0x7C7B7B,
0x8F8E8E, 0xDADADA, 0x828895, 0x2C55B7, 0x3056B7, 0x3056B5, 0x2850AE, 0x1741A5, 0x143EA2, 0x143DA0, 0x133C9D, 0x133C9B, 0xE3698, 0x3F5DA6, 0xCECED3, 0xCCCCD1, 0xD5D3D5, 0x7A87A8, 0x1E4194, 0x3253A5, 0x3454A4, 0x3655A7, 0x3756A8, 0x3756AA, 0x3856AB, 0x3856AB, 0x3756AB, 0x3555AB, 0x3554AC, 0x445993, 0xDADADA, 0x8F8E8E,
0xA9A8A8, 0xC0C0C0, 0xA0A2A6, 0x3759AE, 0x2C54B2, 0x284FAD, 0x1740A3, 0x143EA1, 0x143D9E, 0x123B9B, 0x123B99, 0x133B99, 0xE3696, 0x4C67A8, 0xD1CED0, 0xD3D0D1, 0xD7D4D4, 0x5A6DA0, 0x2B4EA3, 0x3756A7, 0x3A59AB, 0x3B5AAD, 0x3C5AAE, 0x3D5BB0, 0x3D5BB0, 0x3C5AB0, 0x3B59B0, 0x3A57B0, 0x3554B0, 0x7380A2, 0xC1C1C1, 0xA9A8A8,
0xC7C6C6, 0xA1A1A1, 0xD4D4D4, 0x4A629B, 0x284FAD, 0x1840A3, 0x133C9E, 0x133C9D, 0x113A9A, 0x113A98, 0x123897, 0x143C96, 0x183E97, 0x2D4F9D, 0x5E71A1, 0x6173A1, 0x6577A3, 0x3D579D, 0x3756AB, 0x3C5AAE, 0x3D5BB1, 0x3F5CB2, 0x3F5CB5, 0x3F5CB6, 0x3F5BB6, 0x3E5BB6, 0x3D5AB6, 0x3C59B6, 0x3350AC, 0xBEC2CD, 0xA2A2A2, 0xC7C6C6,
0xE9E9E9, 0x7F7E7E, 0xEBEBEB, 0x737B8E, 0x163FA3, 0x123B9D, 0x123B9B, 0x113898, 0xF3896, 0xF3794, 0x123894, 0x173D96, 0x1F4498, 0x1F4499, 0x21469B, 0x3252A2, 0x3151A5, 0x3252AB, 0x3D5BB0, 0x3F5CB4, 0x3F5CB7, 0x3F5CB9, 0x3F5CBA, 0x3F5CBB, 0x3F5BBB, 0x3E5BBB, 0x3D5ABB, 0x3B59BB, 0x475B9E, 0xE9E9EA, 0x807F7F, 0xE9E9E9,
0xFFFFFF, 0xAAA9A9, 0xBFBFBF, 0xB8B8B9, 0x28468D, 0xF3799, 0x113897, 0xF3694, 0xE3692, 0xF3691, 0x133892, 0x1B4096, 0x1E4298, 0x415EA3, 0x98A1B9, 0xB4B5BE, 0xB2B6C5, 0x6F83BF, 0x3A57B4, 0x3F5CB8, 0x3F5CBB, 0x3F5CBE, 0x3F5BC0, 0x3E5BC0, 0x3E5BC1, 0x3E5AC1, 0x3D5AC0, 0x3350BC, 0x9FA5B9, 0xC0C0C0, 0xAAA9A9, 0xFFFFFF,
0xFFFFFF, 0xD9D9D9, 0x8F8E8E, 0xEDEDED, 0x6E7586, 0x93291, 0xE3693, 0xD3591, 0xD3590, 0xF3690, 0x153B92, 0x1D4194, 0x24489A, 0x97A0B9, 0xC2BFBF, 0xB7B6BA, 0xBFBCBF, 0xC7C9D1, 0x4C66B8, 0x3C59BE, 0x3F5AC0, 0x3E5AC2, 0x3D59C4, 0x3D57C5, 0x3B56C5, 0x3B56C4, 0x3855C5, 0x4F61A0, 0xECECEC, 0x8F8E8E, 0xD9D9D9, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xB0B0B0, 0xB8B7B7, 0xD9D9D9, 0x43557D, 0x5308E, 0xC348F, 0xD348E, 0x12378F, 0x193D91, 0x204496, 0x2C4E9D, 0xAFB5C5, 0xC6C4C7, 0xBEBEC3, 0xC2C2C6, 0xD4D3D6, 0x5368B6, 0x3A56C2, 0x3D59C4, 0x3B56C6, 0x3854C9, 0x3754C9, 0x3753C9, 0x3753CA, 0x364EAD, 0xCFD1D7, 0xB9B8B8, 0xB0B0B0, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0x959595, 0xD3D3D3, 0xBFC0C0, 0x384E7F, 0x7308B, 0xD348C, 0x13388E, 0x1D4093, 0x244799, 0x284A9D, 0x7788B8, 0xD4D3D4, 0xD8D6D6, 0xDDDAD9, 0xA4ABC3, 0x3852B4, 0x3B56C6, 0x3854C9, 0x3652CA, 0x3551CC, 0x3350CC, 0x334FCD, 0x344CB7, 0xB2B6C3, 0xD4D4D4, 0x959595, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x908F8F, 0xE1E1E1, 0xBFC0C1, 0x3B4E7A, 0xC338A, 0x153A8F, 0x1F4294, 0x284A9B, 0x3050A1, 0x3353A6, 0x6077B2, 0x8592BA, 0x7282B6, 0x3C54AF, 0x3855C4, 0x3854C9, 0x3551CB, 0x324FCD, 0x304BCE, 0x2E4AD1, 0x364DB0, 0xADB2C2, 0xE1E1E1, 0x908F8F, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD1D1D1, 0x908F8F, 0xD3D3D3, 0xD9D9D9, 0x5D6881, 0x234186, 0x1E4196, 0x2A4B9C, 0x3353A3, 0x3857AB, 0x3655AE, 0x3351B0, 0x3553B8, 0x3C59C3, 0x3B56C6, 0x3652CA, 0x324ECE, 0x2D4AD1, 0x2B45C9, 0x4F5FA2, 0xD0D2D8, 0xD3D3D3, 0x908F8F, 0xD1D1D1, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD8D7D7, 0x959595, 0xB8B7B7, 0xEDEDED, 0xADAFB4, 0x546181, 0x364F8E, 0x3453A2, 0x3859AD, 0x3E5BB2, 0x405CBA, 0x405CC0, 0x3E59C4, 0x3A55CA, 0x3450CE, 0x344BBD, 0x4758A0, 0xA2A7BA, 0xEDEDED, 0xB8B7B7, 0x959595, 0xD8D7D7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xEBEBEB, 0xB0B0B0, 0x8F8E8E, 0xBFBFBF, 0xEBEBEB, 0xCECFD1, 0x888E9D, 0x5E6988, 0x4C5C89, 0x43558F, 0x435594, 0x475894, 0x596696, 0x7E86A4, 0xC6C9D1, 0xEBEBEB, 0xBFBFBF, 0x8F8E8E, 0xB0B0B0, 0xEBEBEB, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xD9D9D9, 0xAAA9A9, 0x7F7E7E, 0xA1A1A1, 0xC0C0C0, 0xDADADA, 0xEDEDED, 0xFBFBFB, 0xFBFBFB, 0xEDEDED, 0xDADADA, 0xC0C0C0, 0xA1A1A1, 0x7F7E7E, 0xAAA9A9, 0xD9D9D9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xE9E9E9, 0xC7C6C6, 0xA9A8A8, 0x8F8E8E, 0x7C7B7B, 0x6F6E6E, 0x6F6E6E, 0x7C7B7B, 0x8F8E8E, 0xA9A8A8, 0xC7C6C6, 0xE9E9E9, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF,
};
const unsigned * rawpx;
switch(ico)
{
case msgbox::icon_information:
rawpx = pic_information;
break;
case msgbox::icon_warning:
rawpx = pic_warning;
break;
case msgbox::icon_error:
rawpx = pic_error;
break;
case msgbox::icon_question:
rawpx = pic_question;
break;
default:
rawpx = nullptr;
}
if(rawpx)
{
nana::paint::pixel_buffer pxbuf(32, 32);
pxbuf.put(reinterpret_cast<const unsigned char*>(rawpx), 32, 32, 32, 4*32, true);
ico_.make(32, 32);
pxbuf.paste(ico_.handle(), 0, 0);
}
}
void _m_click(const arg_mouse& arg)
{
if(arg.window_handle == yes_)
pick_ = (no_.empty() ? msgbox::pick_ok : msgbox::pick_yes);
else if(arg.window_handle == no_)
pick_ = msgbox::pick_no;
else if(arg.window_handle == cancel_)
pick_ = msgbox::pick_cancel;
this->close();
}
private:
window owner_;
place place_;
nana::paint::graphics ico_;
nana::label text_;
nana::button yes_, no_, cancel_;
msgbox::pick_t pick_;
};
#endif
//class msgbox
msgbox::msgbox()
: wd_(nullptr), button_(ok), icon_(icon_none)
{}
msgbox::msgbox(const msgbox& rhs)
: wd_(rhs.wd_), title_(rhs.title_), button_(rhs.button_), icon_(rhs.icon_)
{
sstream_<<rhs.sstream_.str();
}
msgbox& msgbox::operator=(const msgbox& rhs)
{
if(this != &rhs)
{
wd_ = rhs.wd_;
title_ = rhs.title_;
button_ = rhs.button_;
icon_ = rhs.icon_;
sstream_ << rhs.sstream_.str();
}
return *this;
}
msgbox::msgbox(const nana::string& title)
: wd_(nullptr), title_(title), button_(ok), icon_(icon_none)
{}
msgbox::msgbox(window wd, const nana::string& title)
: wd_(wd), title_(title), button_(ok), icon_(icon_none)
{}
msgbox::msgbox(window wd, const nana::string& title, button_t b)
: wd_(wd), title_(title), button_(b), icon_(icon_none)
{}
msgbox& msgbox::icon(icon_t ic)
{
icon_ = ic;
return *this;
}
void msgbox::clear()
{
sstream_.str("");
sstream_.clear();
}
msgbox & msgbox::operator<<(const nana::string& str)
{
#if defined(NANA_UNICODE)
sstream_<<static_cast<std::string>(nana::charset(str));
#else
sstream_<<str;
#endif
return *this;
}
msgbox & msgbox::operator<<(const nana::char_t* str)
{
#if defined(NANA_UNICODE)
sstream_<<static_cast<std::string>(nana::charset(str));;
#else
sstream_<<str;
#endif
return *this;
}
msgbox & msgbox::operator<<(const nana::charset& cs)
{
std::string str = cs;
sstream_ << str;
return *this;
}
msgbox & msgbox::operator<<(std::ostream& (*manipulator)(std::ostream&))
{
sstream_<<manipulator;
return *this;
}
msgbox::pick_t msgbox::show() const
{
#if defined(NANA_WINDOWS)
int type = 0;
switch(button_)
{
case msgbox::ok:
type = MB_OK;
break;
case msgbox::yes_no:
type = MB_YESNO;
break;
case msgbox::yes_no_cancel:
type = MB_YESNOCANCEL;
break;
}
switch(icon_)
{
case msgbox::icon_error:
type |= MB_ICONERROR;
break;
case msgbox::icon_question:
type |= MB_ICONQUESTION;
break;
case msgbox::icon_information:
type |= MB_ICONINFORMATION;
break;
case msgbox::icon_warning:
type |= MB_ICONWARNING;
break;
default: break;
}
#if defined(NANA_UNICODE)
int bt = ::MessageBoxW(reinterpret_cast<HWND>(API::root(wd_)), static_cast<std::wstring>(nana::charset(sstream_.str())).c_str(), title_.c_str(), type);
#else
int bt = ::MessageBoxA(reinterpret_cast<HWND>(API::root(wd_), sstream_.str().c_str(), title_.c_str(), type);
#endif
switch(bt)
{
case IDOK:
return pick_ok;
case IDYES:
return pick_yes;
case IDNO:
return pick_no;
case IDCANCEL:
return pick_cancel;
}
return pick_yes;
#elif defined(NANA_X11)
msgbox_window box(wd_, title_, button_, icon_);
box.prompt(nana::charset(sstream_.str()));
return box.pick();
#endif
return pick_yes;
}
//end class msgbox
}

360
source/gui/notifier.cpp Normal file
View File

@@ -0,0 +1,360 @@
/*
* Implementation of Notifier
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/notifier.cpp
*/
#include <nana/deploy.hpp>
#include <nana/gui/programming_interface.hpp>
#include <nana/gui/notifier.hpp>
#include <nana/gui/timer.hpp>
#include <unordered_map>
#include <unordered_set>
#include <mutex>
#if defined(NANA_WINDOWS)
#include <nana/detail/win32/platform_spec.hpp>
#elif defined(NANA_LINUX)
#include PLATFORM_SPEC_HPP
#include <nana/system/platform.hpp>
#include <iostream>
#endif
namespace nana
{
typedef std::lock_guard<std::recursive_mutex> lock_guard;
struct notifier::implement
{
nana::timer ani_timer;
native_window_type native_handle;
window handle;
event_handle evt_destroy;
unsigned short id;
detail::notifier_events events;
bool icon_added = false;
std::size_t play_index;
#if defined(NANA_WINDOWS)
HICON icon_handle = nullptr;
std::vector<HICON> icons;
void set_icon(HICON icon)
{
if (icon_handle)
{
NOTIFYICONDATA icon_data;
memset(&icon_data, 0, sizeof icon_data);
icon_data.cbSize = sizeof icon_data;
icon_data.hWnd = reinterpret_cast<HWND>(native_handle);
icon_data.uID = id;
icon_data.uFlags = NIF_MESSAGE | NIF_ICON;
icon_data.uCallbackMessage = nana::detail::messages::tray;
icon_data.hIcon = icon;
::Shell_NotifyIcon(icon_added ? NIM_MODIFY : NIM_ADD, &icon_data);
icon_added = true;
}
}
#endif
};
arg_notifier::operator nana::arg_mouse() const
{
arg_mouse arg;
arg.evt_code = evt_code;
arg.ctrl = false;
arg.shift = false;
arg.left_button = left_button;
arg.mid_button = mid_button;
arg.right_button = right_button;
arg.pos = pos;
arg.window_handle = (notifier_ptr ? notifier_ptr->handle() : nullptr);
return arg;
}
class notifications
{
struct notifier_data
{
::nana::notifier * notifier_ptr;
detail::notifier_events* evt_ptr;
};
struct window_notifiers
{
std::unordered_map<unsigned short, notifier_data> idtable;
};
public:
static notifications& instance()
{
static notifications obj;
return obj;
}
unsigned short register_wd(::nana::notifier* ntf_ptr, native_window_type native_handle, detail::notifier_events* evt_ptr)
{
lock_guard lock(mutex_);
auto i = wd_table_.find(native_handle);
if (i == wd_table_.end())
{
auto & data = wd_table_[native_handle].idtable[0];
data.notifier_ptr = ntf_ptr;
data.evt_ptr = evt_ptr;
return 0;
}
auto & idtable = i->second.idtable;
auto id = static_cast<unsigned short>(idtable.size());
const auto orig_id = id;
while (idtable.count(id) != 0)
{
if (orig_id == ++id)
throw std::runtime_error("Full");
}
auto & data = idtable[id];
data.notifier_ptr = ntf_ptr;
data.evt_ptr = evt_ptr;
return id;
}
void cancel(native_window_type wd, unsigned short id)
{
lock_guard lock(mutex_);
auto i_wd = wd_table_.find(wd);
if (i_wd == wd_table_.end())
return;
auto & idtable = i_wd->second.idtable;
auto i_id = idtable.find(id);
if (i_id == idtable.end())
return;
idtable.erase(i_id);
if (idtable.empty())
wd_table_.erase(i_wd);
}
void emit(native_window_type wd, unsigned short id, const arg_notifier& arg_basic)
{
lock_guard lock(mutex_);
auto i_wd = wd_table_.find(wd);
if (i_wd == wd_table_.end())
return;
auto & idtable = i_wd->second.idtable;
auto i_id = idtable.find(id);
if (i_id == idtable.end())
return;
auto arg = arg_basic;
arg.notifier_ptr = i_id->second.notifier_ptr;
auto evt_ptr = i_id->second.evt_ptr;
switch (arg.evt_code)
{
case event_code::mouse_down:
evt_ptr->mouse_down.emit(arg);
break;
case event_code::mouse_up:
evt_ptr->mouse_up.emit(arg);
break;
case event_code::mouse_leave:
evt_ptr->mouse_leave.emit(arg);
break;
case event_code::mouse_move:
evt_ptr->mouse_move.emit(arg);
break;
case event_code::dbl_click:
evt_ptr->dbl_click.emit(arg);
break;
default:
break;
}
}
private:
mutable std::recursive_mutex mutex_;
std::unordered_map<native_window_type, window_notifiers> wd_table_;
};
#if defined(NANA_WINDOWS)
void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam)
{
arg_notifier arg;
switch (lparam)
{
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
arg.evt_code = event_code::dbl_click;
arg.left_button = (lparam == WM_LBUTTONDBLCLK);
arg.mid_button = (lparam == WM_MBUTTONDBLCLK);
arg.right_button = (lparam == WM_RBUTTONDBLCLK);
break;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
arg.evt_code = event_code::mouse_down;
arg.left_button = (lparam == WM_LBUTTONDOWN);
arg.mid_button = (lparam == WM_MBUTTONDOWN);
arg.right_button = (lparam == WM_RBUTTONDOWN);
break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
arg.evt_code = event_code::mouse_up;
arg.left_button = (lparam == WM_LBUTTONUP);
arg.mid_button = (lparam == WM_MBUTTONUP);
arg.right_button = (lparam == WM_RBUTTONUP);
break;
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
arg.evt_code = (WM_MOUSEMOVE == lparam ? event_code::mouse_move : event_code::mouse_leave);
arg.left_button = false;
arg.mid_button = false;
arg.right_button = false;
break;
default:
return;
}
::POINT pos;
::GetCursorPos(&pos);
arg.pos.x = pos.x;
arg.pos.y = pos.y;
notifications::instance().emit(reinterpret_cast<native_window_type>(wd), static_cast<unsigned short>(wparam), arg);
}
#endif
typedef ::nana::detail::bedrock bedrock;
//class notifier
notifier::notifier(window wd)
: impl_(new implement)
{
impl_->ani_timer.elapse([this]
{
#if defined(NANA_WINDOWS)
if (impl_->play_index >= impl_->icons.size())
impl_->play_index = 0;
auto ico = impl_->icons[impl_->play_index++];
impl_->set_icon(ico);
#endif
});
auto & brock = bedrock::instance();
impl_->handle = wd;
impl_->native_handle = brock.root(reinterpret_cast<bedrock::core_window_t*>(wd));
impl_->evt_destroy = API::events(wd).destroy([this]
{
close();
});
impl_->id = notifications::instance().register_wd(this, impl_->native_handle, &impl_->events);
}
notifier::~notifier()
{
close();
delete impl_;
}
void notifier::close()
{
if (nullptr == impl_->native_handle)
return;
#if defined(NANA_WINDOWS)
NOTIFYICONDATA icon_data;
memset(&icon_data, 0, sizeof icon_data);
icon_data.cbSize = sizeof icon_data;
icon_data.hWnd = reinterpret_cast<HWND>(impl_->native_handle);
icon_data.uID = impl_->id;
::Shell_NotifyIcon(NIM_DELETE, &icon_data);
if (impl_->icon_handle)
::DestroyIcon(impl_->icon_handle);
for (auto handle : impl_->icons)
::DestroyIcon(handle);
#endif
API::umake_event(impl_->evt_destroy);
notifications::instance().cancel(impl_->native_handle, impl_->id);
impl_->native_handle = nullptr;
}
void notifier::text(const nana::string& str)
{
#if defined(NANA_WINDOWS)
NOTIFYICONDATA icon_data;
memset(&icon_data, 0, sizeof icon_data);
icon_data.cbSize = sizeof icon_data;
icon_data.hWnd = reinterpret_cast<HWND>(impl_->native_handle);
icon_data.uID = impl_->id;
icon_data.uFlags = NIF_MESSAGE | NIF_TIP;
icon_data.uCallbackMessage = nana::detail::messages::tray;
strcpy(icon_data.szTip, str.data());
::Shell_NotifyIcon(impl_->icon_added ? NIM_MODIFY : NIM_ADD, &icon_data);
impl_->icon_added = true;
#endif
}
void notifier::icon(const nana::string& icon_file)
{
#if defined(NANA_WINDOWS)
auto pre_icon = impl_->icon_handle;
auto ico = (HICON)::LoadImage(0, icon_file.data(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
if (ico)
{
impl_->icon_handle = ico;
impl_->ani_timer.stop();
impl_->play_index = 0;
impl_->set_icon(impl_->icon_handle);
::DestroyIcon(pre_icon);
}
#endif
}
void notifier::insert_icon(const nana::string& icon_file)
{
#if defined(NANA_WINDOWS)
auto icon = (HICON)::LoadImage(0, icon_file.data(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
impl_->icons.push_back(icon);
#endif
}
void notifier::period(unsigned ms)
{
#if defined(NANA_WINDOWS)
if (ms && impl_->icons.size())
{
ms /= static_cast<unsigned>(impl_->icons.size());
impl_->ani_timer.interval(ms < 16 ? 16 : ms);
impl_->ani_timer.start();
}
else
impl_->ani_timer.stop();
#endif
}
detail::notifier_events& notifier::events()
{
return impl_->events;
}
window notifier::handle() const
{
return impl_->handle;
}
//end notifier
}

2219
source/gui/place.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
/*
* State Cursor
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/state_cursor.cpp
*/
#include <nana/gui/state_cursor.hpp>
#include <nana/gui/detail/bedrock.hpp>
#include <nana/gui/detail/basic_window.hpp>
namespace nana
{
state_cursor::state_cursor(window handle, cursor cur)
: handle_(handle)
{
auto & brock = detail::bedrock::instance();
auto wd = reinterpret_cast<detail::basic_window*>(handle);
if (brock.wd_manager.available(wd))
brock.define_state_cursor(wd, cur, nullptr);
else
handle_ = nullptr;
}
state_cursor::state_cursor(state_cursor&& rhs)
: handle_(rhs.handle_)
{
rhs.handle_ = nullptr;
}
state_cursor& state_cursor::operator = (state_cursor&& rhs)
{
if (this != &rhs)
{
if (handle_)
{
auto & brock = detail::bedrock::instance();
auto wd = reinterpret_cast<detail::basic_window*>(handle_);
if (brock.wd_manager.available(wd))
brock.undefine_state_cursor(wd, nullptr);
}
handle_ = rhs.handle_;
rhs.handle_ = nullptr;
}
return *this;
}
state_cursor::~state_cursor()
{
if (handle_)
{
auto & brock = detail::bedrock::instance();
auto wd = reinterpret_cast<detail::basic_window*>(handle_);
if (brock.wd_manager.available(wd))
brock.undefine_state_cursor(wd, nullptr);
}
}
}

238
source/gui/timer.cpp Normal file
View File

@@ -0,0 +1,238 @@
/*
* A Timer Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/timer.hpp
* @description:
* A timer can repeatedly call a piece of code. The duration between
* calls is specified in milliseconds. Timer is defferent from other graphics
* controls, it has no graphics interface.
*/
#include <nana/deploy.hpp>
#include <nana/gui/timer.hpp>
#include <map>
#include <memory>
#if defined(NANA_MINGW) && defined(STD_THREAD_NOT_SUPPORTED)
#include <nana/std_mutex.hpp>
#else
#include <mutex>
#endif
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_LINUX)
#include PLATFORM_SPEC_HPP
#include <nana/system/platform.hpp>
#endif
namespace nana
{
class timer_core;
#if defined(NANA_WINDOWS)
typedef UINT_PTR timer_identifier;
#else
typedef timer_core* timer_identifier;
#endif
class timer_driver
{
typedef std::lock_guard<std::recursive_mutex> lock_guard;
friend class timer_core;
timer_driver()
{}
public:
static timer_driver& instance()
{
static timer_driver obj;
return obj;
}
template<typename Factory>
timer_core* create(unsigned ms, Factory && factory)
{
#if defined(NANA_WINDOWS)
auto tmid = ::SetTimer(nullptr, 0, ms, &timer_driver::_m_timer_proc);
#endif
try
{
#if defined(NANA_WINDOWS)
auto p = factory(tmid);
#else
auto p = factory();
auto tmid = p;
::nana::detail::platform_spec::instance().set_timer(reinterpret_cast<std::size_t>(tmid), ms, &timer_driver::_m_timer_proc);
#endif
lock_guard lock(mutex_);
timer_table_[tmid].reset(p);
return p;
}
catch (...)
{
}
return nullptr;
}
void destroy(timer_identifier tid)
{
lock_guard lock(mutex_);
auto i = timer_table_.find(tid);
if (i != timer_table_.end())
{
#if defined(NANA_WINDOWS)
::KillTimer(nullptr, tid);
#else
::nana::detail::platform_spec::instance().kill_timer(reinterpret_cast<std::size_t>(tid));
#endif
timer_table_.erase(i);
}
}
private:
#if defined(NANA_WINDOWS)
static void __stdcall _m_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime);
#else
static void _m_timer_proc(std::size_t id);
#endif
private:
std::recursive_mutex mutex_;
std::map<timer_identifier, std::unique_ptr<timer_core>> timer_table_;
};
class timer_core
{
public:
#if defined(NANA_WINDOWS)
timer_core(timer_identifier tmid, const nana::basic_event<arg_elapse>& evt_elapse)
: timer_(tmid), evt_elapse_(evt_elapse)
{}
#else
timer_core(const nana::basic_event<arg_elapse>& evt_elapse)
: timer_(this), evt_elapse_(evt_elapse)
{}
#endif
timer_identifier id() const
{
return timer_;
}
void interval(unsigned ms)
{
#if defined(NANA_WINDOWS)
::SetTimer(nullptr, timer_, ms, &timer_driver::_m_timer_proc);
#else
::nana::detail::platform_spec::instance().set_timer(reinterpret_cast<std::size_t>(timer_), ms, &timer_driver::_m_timer_proc);
#endif
}
void emit(const arg_elapse& arg)
{
evt_elapse_.emit(arg);
}
private:
const timer_identifier timer_;
const nana::basic_event<arg_elapse> & evt_elapse_;
}; //end class timer_core
#if defined(NANA_WINDOWS)
void __stdcall timer_driver::_m_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime)
#else
void timer_driver::_m_timer_proc(std::size_t id)
#endif
{
auto & driver = instance();
lock_guard lock(driver.mutex_);
#if defined(NANA_WINDOWS)
auto i = driver.timer_table_.find(id);
#else
auto i = driver.timer_table_.find(reinterpret_cast<timer_identifier>(id));
#endif
if (i == driver.timer_table_.end())
return;
arg_elapse arg;
arg.id = id;
i->second->emit(arg);
}
struct timer::implement
{
unsigned interval = 1000; //Defaultly 1 second.
timer_core * tm_core = nullptr;
};
//class timer
timer::timer()
: impl_(new implement)
{
}
timer::~timer()
{
if (impl_->tm_core)
timer_driver::instance().destroy(impl_->tm_core->id());
delete impl_;
}
void timer::reset()
{
stop();
elapse_.clear();
}
void timer::start()
{
if (impl_->tm_core)
return;
#if defined(NANA_WINDOWS)
impl_->tm_core = timer_driver::instance().create(impl_->interval, [this](timer_identifier id)
{
return new timer_core(id, elapse_);
});
#else
impl_->tm_core = timer_driver::instance().create(impl_->interval, [this]
{
return new timer_core(elapse_);
});
#endif
}
bool timer::started() const
{
return (nullptr != impl_->tm_core);
}
void timer::stop()
{
if (nullptr == impl_->tm_core)
return;
auto tmid = impl_->tm_core->id();
impl_->tm_core = nullptr;
timer_driver::instance().destroy(tmid);
}
void timer::interval(unsigned ms)
{
if (ms != impl_->interval)
{
impl_->interval = ms;
if (impl_->tm_core)
impl_->tm_core->interval(ms);
}
}
unsigned timer::interval() const
{
return impl_->interval;
}
//end class timer
}//end namespace nana

355
source/gui/tooltip.cpp Normal file
View File

@@ -0,0 +1,355 @@
/*
* A Tooltip Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/tooltip.cpp
*/
#include <nana/gui/tooltip.hpp>
#include <nana/gui/widgets/label.hpp>
#include <nana/gui/timer.hpp>
#include <memory>
namespace nana
{
namespace drawerbase
{
namespace tooltip
{
class drawer
: public drawer_trigger
{
private:
void refresh(graph_reference graph)
{
graph.rectangle(0x0, false);
graph.rectangle(1, 1, graph.width() - 2, graph.height() - 2, 0xF0F0F0, true);
}
};
nana::point pos_by_screen(nana::point pos, const nana::size& sz, bool overlap_allowed)
{
auto scr_area = API::screen_area_from_point(pos);
if (pos.x + sz.width > scr_area.x + scr_area.width)
pos.x = static_cast<int>(scr_area.x + scr_area.width - sz.width);
if (pos.x < scr_area.x)
pos.x = scr_area.x;
if (pos.y + sz.height >= scr_area.y + scr_area.height)
pos.y = static_cast<int>(scr_area.y + scr_area.height - sz.height);
else if (!overlap_allowed)
pos.y += 20; //Add some pixels to avoid overlapping between cursor and tip window.
if (pos.y < scr_area.y)
pos.y = scr_area.y;
return pos;
}
class tip_form
: public widget_object<category::root_tag, drawer>,
public tooltip_interface
{
typedef widget_object<category::root_tag, drawer> base_type;
public:
tip_form()
: base_type(nana::rectangle(), appear::bald<appear::floating>()),
duration_(0)
{
API::take_active(this->handle(), false, nullptr);
label_.create(*this);
label_.format(true);
label_.transparent(true);
}
private:
//tooltip_interface implementation
bool tooltip_empty() const override
{
return this->empty();
}
void tooltip_text(const nana::string& text) override
{
label_.caption(text);
auto text_s = label_.measure(API::screen_size().width * 2 / 3);
this->size(nana::size{ text_s.width + 10, text_s.height + 10 });
label_.move(rectangle{ 5, 5, text_s.width, text_s.height });
timer_.reset();
if (duration_)
{
timer_.interval(static_cast<unsigned>(duration_));
timer_.elapse(std::bind(&tip_form::_m_tick_duration, this));
}
else
{
timer_.interval(500);
timer_.elapse(std::bind(&tip_form::_m_tick, this));
}
timer_.start();
}
virtual nana::size tooltip_size() const override
{
return this->size();
}
virtual void tooltip_move(const nana::point& scr_pos, bool ignore_pos) override
{
ignore_pos_ = ignore_pos;
pos_ = scr_pos;
if (duration_)
{
this->move(scr_pos.x, scr_pos.y);
this->show();
}
}
virtual void duration(std::size_t d) override
{
duration_ = d;
timer_.reset();
}
private:
void _m_tick()
{
nana::point pos;
if (ignore_pos_)
{
pos = API::cursor_position();
//The cursor must be stay here for half second.
if (pos != pos_)
{
pos_ = pos;
return;
}
pos = pos_by_screen(pos, size(), false);
}
else
pos = pos_;
timer_.stop();
move(pos.x, pos.y);
show();
}
void _m_tick_duration()
{
timer_.reset();
this->close();
}
private:
timer timer_;
nana::label label_;
nana::point pos_;
bool ignore_pos_;
std::size_t duration_;
};//end class tip_form
class controller
{
typedef std::pair<window, nana::string> pair_t;
typedef std::function<void(tooltip_interface*)> deleter_type;
class tip_form_factory
: public nana::tooltip::factory_if_type
{
tooltip_interface * create() override
{
return new tip_form;
}
void destroy(tooltip_interface* p) override
{
delete p;
}
};
public:
static std::shared_ptr<nana::tooltip::factory_if_type>& factory()
{
static std::shared_ptr<nana::tooltip::factory_if_type> fp;
if (nullptr == fp)
fp = std::make_shared<tip_form_factory>();
return fp;
}
//external synchronization.
static controller* instance(bool destroy = false)
{
static controller* ptr;
if(destroy)
{
delete ptr;
ptr = nullptr;
}
else if(nullptr == ptr)
{
ptr = new controller;
}
return ptr;
}
void set(window wd, const nana::string& str)
{
if (str.empty())
_m_untip(wd);
else
_m_get(wd).second = str;
}
void show(const nana::string& text)
{
if (nullptr == window_ || window_->tooltip_empty())
{
auto fp = factory();
window_ = std::unique_ptr<tooltip_interface, deleter_type>(fp->create(), [fp](tooltip_interface* ti)
{
fp->destroy(ti);
});
}
window_->duration(0);
window_->tooltip_text(text);
window_->tooltip_move(API::cursor_position(), true);
}
void show_duration(window wd, point pos, const nana::string& text, std::size_t duration)
{
if (nullptr == window_ || window_->tooltip_empty())
{
auto fp = factory();
window_ = std::unique_ptr<tooltip_interface, deleter_type>(fp->create(), [fp](tooltip_interface* ti)
{
fp->destroy(ti);
});
}
window_->duration(duration);
window_->tooltip_text(text);
pos = pos_by_screen(pos, window_->tooltip_size(), true);
window_->tooltip_move(pos, false);
}
void close()
{
window_.reset();
//Destroy the tooltip controller when there are not tooltips.
if (cont_.empty())
instance(true);
}
private:
void _m_enter(const arg_mouse& arg)
{
pair_t & pr = _m_get(arg.window_handle);
if(pr.second.size())
{
this->show(pr.second);
}
}
void _m_leave(const arg_mouse&)
{
close();
}
void _m_destroy(const arg_destroy& arg)
{
_m_untip(arg.window_handle);
}
void _m_untip(window wd)
{
for (auto i = cont_.begin(); i != cont_.end(); ++i)
{
if (i->first == wd)
{
cont_.erase(i);
if (cont_.empty())
{
window_.reset();
instance(true);
}
return;
}
}
}
private:
pair_t& _m_get(window wd)
{
for (auto & pr : cont_)
{
if (pr.first == wd)
return pr;
}
auto & events = API::events(wd);
events.mouse_enter.connect([this](const arg_mouse& arg){
_m_enter(arg);
});
auto leave_fn = std::bind(&controller::_m_leave, this, std::placeholders::_1);
events.mouse_leave.connect(leave_fn);
events.mouse_down.connect(leave_fn);
events.destroy.connect([this](const arg_destroy& arg){
_m_destroy(arg);
});
cont_.emplace_back(wd, nana::string());
return cont_.back();
}
private:
std::unique_ptr<tooltip_interface, deleter_type> window_;
std::vector<pair_t> cont_;
};
}//namespace tooltip
}//namespace drawerbase
//class tooltip
typedef drawerbase::tooltip::controller ctrl;
void tooltip::set(window wd, const nana::string& text)
{
if(false == API::empty_window(wd))
{
internal_scope_guard lock;
ctrl::instance()->set(wd, text);
}
}
void tooltip::show(window wd, point pos, const nana::string& text, std::size_t duration)
{
internal_scope_guard lock;
API::calc_screen_point(wd, pos);
ctrl::instance()->show_duration(wd, pos, text, duration);
}
void tooltip::close()
{
internal_scope_guard lock;
ctrl::instance()->close();
}
void tooltip::_m_hold_factory(factory_interface* p)
{
ctrl::factory().reset(p);
}
//end class tooltip
}//namespace nana

View File

@@ -0,0 +1,492 @@
/*
* A Button Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/button.cpp
*/
#include <nana/gui/widgets/button.hpp>
#include <nana/paint/text_renderer.hpp>
namespace nana{ namespace drawerbase
{
namespace button
{
//trigger
//@brief: draw the button
trigger::trigger()
: widget_(nullptr),
graph_(nullptr),
cite_("button")
{
attr_.e_state = element_state::normal;
attr_.omitted = attr_.focused = attr_.pushed = attr_.enable_pushed = attr_.keep_pressed = false;
attr_.focus_color = true;
attr_.icon = nullptr;
}
trigger::~trigger()
{
delete attr_.icon;
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
graph_ = &graph;
widget_ = &widget;
window wd = widget;
API::tabstop(wd);
API::effects_edge_nimbus(wd, effects::edge_nimbus::active);
API::effects_edge_nimbus(wd, effects::edge_nimbus::over);
}
bool trigger::enable_pushed(bool eb)
{
attr_.enable_pushed = eb;
return((eb == false) && pushed(false));
}
bool trigger::pushed(bool pshd)
{
if(pshd != attr_.pushed)
{
attr_.pushed = pshd;
if(false == pshd)
{
if (API::find_window(API::cursor_position()) == widget_->handle())
attr_.e_state = element_state::hovered;
else
attr_.e_state = element_state::normal;
}
else
attr_.e_state = element_state::pressed;
return true;
}
return false;
}
bool trigger::pushed() const
{
return attr_.pushed;
}
void trigger::omitted(bool om)
{
attr_.omitted = om;
}
bool trigger::focus_color(bool eb)
{
if(eb != attr_.focus_color)
{
attr_.focus_color = eb;
return true;
}
return false;
}
element::cite_bground & trigger::cite()
{
return cite_;
}
void trigger::refresh(graph_reference graph)
{
_m_draw(graph);
}
void trigger::mouse_enter(graph_reference graph, const arg_mouse&)
{
attr_.e_state = (attr_.pushed || attr_.keep_pressed ? element_state::pressed : element_state::hovered);
_m_draw(graph);
API::lazy_refresh();
}
void trigger::mouse_leave(graph_reference graph, const arg_mouse&)
{
if(attr_.enable_pushed && attr_.pushed)
return;
attr_.e_state = element_state::normal;
_m_draw(graph);
API::lazy_refresh();
}
void trigger::mouse_down(graph_reference graph, const arg_mouse&)
{
attr_.e_state = element_state::pressed;
attr_.keep_pressed = true;
_m_draw(graph);
API::capture_window(*widget_, true);
API::lazy_refresh();
}
void trigger::mouse_up(graph_reference graph, const arg_mouse&)
{
API::capture_window(*widget_, false);
attr_.keep_pressed = false;
if(attr_.enable_pushed && (false == attr_.pushed))
{
attr_.pushed = true;
}
else
{
if (element_state::pressed == attr_.e_state)
attr_.e_state = element_state::hovered;
else
attr_.e_state = element_state::normal;
attr_.pushed = false;
_m_draw(graph);
API::lazy_refresh();
}
}
void trigger::key_char(graph_reference, const arg_keyboard& arg)
{
if(arg.key == static_cast<char_t>(keyboard::enter))
emit_click();
}
void trigger::key_press(graph_reference, const arg_keyboard& arg)
{
bool ch_tabstop_next;
switch(arg.key)
{
case keyboard::os_arrow_left: case keyboard::os_arrow_up:
ch_tabstop_next = false;
break;
case keyboard::os_arrow_right: case keyboard::os_arrow_down:
ch_tabstop_next = true;
break;
default:
return;
}
API::move_tabstop(widget_->handle(), ch_tabstop_next);
}
void trigger::focus(graph_reference graph, const arg_focus& arg)
{
attr_.focused = arg.getting;
_m_draw(graph);
API::lazy_refresh();
}
void trigger::_m_draw_title(graph_reference graph, bool enabled)
{
nana::string text = widget_->caption();
nana::string::value_type shortkey;
nana::string::size_type shortkey_pos;
nana::string str = API::transform_shortkey_text(text, shortkey, &shortkey_pos);
nana::size ts = graph.text_extent_size(str);
nana::size gsize = graph.size();
nana::size icon_sz;
if(attr_.icon)
{
icon_sz = attr_.icon->size();
icon_sz.width += 5;
}
int x = (static_cast<int>(gsize.width - 1 - ts.width) >> 1);
int y = (static_cast<int>(gsize.height - 1 - ts.height) >> 1);
if(x < static_cast<int>(icon_sz.width))
x = static_cast<int>(icon_sz.width);
unsigned omitted_pixels = gsize.width - icon_sz.width;
std::size_t txtlen = str.size();
const nana::char_t* txtptr = str.c_str();
if(ts.width)
{
nana::paint::text_renderer tr(graph);
if(enabled)
{
if (element_state::pressed == attr_.e_state)
{
++x;
++y;
}
color_t fgcolor = (attr_.focus_color ? (attr_.focused ? 0xFF : attr_.fgcolor) : attr_.fgcolor);
if(attr_.omitted)
tr.render(x, y, fgcolor, txtptr, txtlen, omitted_pixels, true);
else
graph.bidi_string(x, y, fgcolor, txtptr, txtlen);
if(shortkey)
{
unsigned off_w = (shortkey_pos ? graph.text_extent_size(str, static_cast<unsigned>(shortkey_pos)).width : 0);
nana::size shortkey_size = graph.text_extent_size(txtptr + shortkey_pos, 1);
x += off_w;
y += shortkey_size.height;
graph.line(x, y, x + shortkey_size.width - 1, y, 0x0);
}
}
else
{
if(attr_.omitted)
{
tr.render(x + 1, y + 1, 0xFFFFFF, txtptr, txtlen, omitted_pixels, true);
tr.render(x, y, 0x808080, txtptr, txtlen, omitted_pixels, true);
}
else
{
graph.bidi_string(x + 1, y + 1, 0xFFFFFF, txtptr, txtlen);
graph.bidi_string(x, y, 0x808080, txtptr, txtlen);
}
}
}
if(attr_.icon)
attr_.icon->paste(graph, 3, (gsize.height - icon_sz.height) / 2);
}
void trigger::_m_draw(graph_reference graph)
{
window wd = widget_->handle();
bool eb = API::window_enabled(wd);
attr_.bgcolor = API::background(wd);
attr_.fgcolor = API::foreground(wd);
element_state e_state = attr_.e_state;
if (eb)
{
if (attr_.focused)
{
if (element_state::normal == e_state)
e_state = element_state::focus_normal;
else if (element_state::hovered == e_state)
e_state = element_state::focus_hovered;
}
}
else
e_state = element_state::disabled;
if (false == cite_.draw(graph, attr_.bgcolor, attr_.fgcolor, graph.size(), e_state))
{
if (bground_mode::basic != API::effects_bground_mode(wd))
{
_m_draw_background(graph);
_m_draw_border(graph);
}
}
_m_draw_title(graph, eb);
}
void trigger::_m_draw_background(graph_reference graph)
{
nana::rectangle r(graph.size());
r.pare_off(1);
nana::color_t color_start = nana::paint::graphics::mix(attr_.bgcolor, 0xFFFFFF, 0.2);
nana::color_t color_end = nana::paint::graphics::mix(attr_.bgcolor, 0x0, 0.95);
if (element_state::pressed == attr_.e_state)
{
r.x = r.y = 2;
std::swap(color_start, color_end);
}
graph.shadow_rectangle(r, color_start, color_end, true);
}
void trigger::_m_draw_border(graph_reference graph)
{
nana::rectangle r(graph.size());
int right = r.width - 1;
int bottom = r.height - 1;
graph.rectangle_line(r,
0x7F7F7F, 0x7F7F7F, 0x707070, 0x707070);
graph.set_pixel(1, 1, 0x919191);
graph.set_pixel(right - 1, 1, 0x919191);
graph.set_pixel(right - 1, bottom - 1, 0x919191);
graph.set_pixel(1, bottom - 1, 0x919191);
graph.set_pixel(0, 0, color::button_face);
graph.set_pixel(right, 0, color::button_face);
graph.set_pixel(0, bottom, color::button_face);
graph.set_pixel(right, bottom, color::button_face);
if (element_state::pressed == attr_.e_state)
graph.rectangle(r.pare_off(1), 0xC3C3C3, false);
}
void trigger::emit_click()
{
arg_mouse arg;
arg.evt_code = event_code::click;
arg.window_handle = widget_->handle();
arg.ctrl = arg.shift = false;
arg.mid_button = arg.right_button = false;
arg.left_button = true;
arg.pos.x = arg.pos.y = 1;
API::emit_event(event_code::click, arg.window_handle, arg);
}
void trigger::icon(const nana::paint::image& img)
{
if(img.empty()) return;
if(nullptr == attr_.icon)
attr_.icon = new paint::image;
*attr_.icon = img;
}
//end class trigger
}//end namespace button
}//end namespace drawerbase
//button
//@brief: Defaine a button widget and it provides the interfaces to be operational
button::button(){}
button::button(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
button::button(window wd, const nana::string& text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
button::button(window wd, const nana::char_t* text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
button::button(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
button& button::icon(const nana::paint::image& img)
{
internal_scope_guard isg;
get_drawer_trigger().icon(img);
API::refresh_window(handle());
return *this;
}
button& button::enable_pushed(bool eb)
{
internal_scope_guard isg;
if(get_drawer_trigger().enable_pushed(eb))
API::refresh_window(handle());
return *this;
}
bool button::pushed() const
{
return get_drawer_trigger().pushed();
}
button& button::pushed(bool psd)
{
internal_scope_guard isg;
if(get_drawer_trigger().pushed(psd))
API::refresh_window(handle());
return *this;
}
button& button::omitted(bool om)
{
internal_scope_guard isg;
get_drawer_trigger().omitted(om);
API::refresh_window(handle());
return *this;
}
button& button::enable_focus_color(bool eb)
{
internal_scope_guard lock;
if(get_drawer_trigger().focus_color(eb))
API::refresh_window(handle());
return *this;
}
button& button::set_bground(const pat::cloneable<element::element_interface>& rv)
{
internal_scope_guard lock;
get_drawer_trigger().cite().set(rv);
return *this;
}
button& button::set_bground(const std::string& name)
{
internal_scope_guard lock;
get_drawer_trigger().cite().set(name.data());
return *this;
}
button& button::transparent(bool enabled)
{
if (enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
return *this;
}
bool button::transparent() const
{
return (bground_mode::basic == API::effects_bground_mode(*this));
}
button& button::edge_effects(bool enable)
{
if (enable)
{
API::effects_edge_nimbus(*this, effects::edge_nimbus::active);
API::effects_edge_nimbus(*this, effects::edge_nimbus::over);
}
else
API::effects_edge_nimbus(*this, effects::edge_nimbus::none);
return *this;
}
void button::_m_shortkey()
{
get_drawer_trigger().emit_click();
}
void button::_m_complete_creation()
{
events().shortkey.connect([this]
{
_m_shortkey();
});
}
void button::_m_caption(nana::string&& text)
{
API::unregister_shortkey(handle());
nana::char_t shortkey;
API::transform_shortkey_text(text, shortkey, 0);
if (shortkey)
API::register_shortkey(handle(), shortkey);
base_type::_m_caption(std::move(text));
}
//end class button
}//end namespace nana

View File

@@ -0,0 +1,942 @@
/*
* A Categorize Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/categorize.cpp
*/
#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/categorize.hpp>
#include <nana/gui/widgets/float_listbox.hpp>
#include <nana/paint/gadget.hpp>
#include <nana/gui/widgets/detail/tree_cont.hpp>
#include <stdexcept>
namespace nana
{
namespace drawerbase
{
namespace categorize
{
struct event_agent_holder
{
std::function<void(nana::any&)> selected;
};
struct item
: public float_listbox::item_interface
{
nana::paint::image item_image;
nana::string item_text;
public:
item(const nana::string& s)
: item_text(s)
{}
public:
//Implement item_interface methods
const nana::paint::image& image() const override
{
return item_image;
}
const nana::char_t * text() const override
{
return item_text.data();
}
};
struct item_tag
{
nana::size scale;
unsigned pixels;
nana::any value;
};
//class renderer
renderer::ui_element::ui_element()
: what(none), index(0)
{}
renderer::~renderer(){}
//end class renderer
//interior_renderer
class interior_renderer
: public renderer
{
private:
void background(graph_reference graph, window wd, const nana::rectangle& r, const ui_element& ue)
{
ui_el_ = ue;
style_.bgcolor = API::background(wd);
style_.fgcolor = API::foreground(wd);
if(ue.what == ue.none || (API::window_enabled(wd) == false))
{ //the mouse is out of the widget.
style_.bgcolor = nana::paint::graphics::mix(style_.bgcolor, 0xA0C9F5, 0.9);
}
graph.rectangle(r, style_.bgcolor, true);
}
virtual void root_arrow(graph_reference graph, const nana::rectangle& r, mouse_action state)
{
int x = r.x + (r.width - 16) / 2;
int y = r.y + (r.height - 16) / 2;
if(ui_el_.what == ui_el_.item_root)
{
_m_item_bground(graph, r.x + 1, r.y, r.width - 2, r.height, (state == mouse_action::pressed ? mouse_action::pressed : mouse_action::over));
graph.rectangle(r, 0x3C7FB1, false);
if(state == mouse_action::pressed)
{
++x;
++y;
}
}
else
graph.rectangle(r, style_.bgcolor, true);
nana::paint::gadget::arrow_16_pixels(graph, x, y,
style_.fgcolor, 3, nana::paint::gadget::directions::to_west);
}
void item(graph_reference graph, const nana::rectangle& r, std::size_t index, const nana::string& name, unsigned txtheight, bool has_child, mouse_action state)
{
nana::point strpos(r.x + 5, r.y + static_cast<int>(r.height - txtheight) / 2);
if((ui_el_.what == ui_el_.item_arrow || ui_el_.what == ui_el_.item_name) && (ui_el_.index == index))
{
mouse_action state_arrow, state_name;
if(mouse_action::pressed != state)
{
state_arrow = (ui_el_.what == ui_el_.item_arrow ? mouse_action::over : mouse_action::normal);
state_name = (ui_el_.what == ui_el_.item_name ? mouse_action::over : mouse_action::normal);
}
else
{
state_name = state_arrow = mouse_action::pressed;
++strpos.x;
++strpos.y;
}
int top = r.y + 1;
unsigned width = r.width - 2;
unsigned height = r.height - 2;
if(has_child)
{
int left = r.x + r.width - 16;
_m_item_bground(graph, left, top, 15, height, state_arrow);
width -= 16;
--left;
graph.line(left, top, left, r.y + height, 0x3C7FB1);
}
_m_item_bground(graph, r.x + 1, top, width, height, state_name);
graph.rectangle(r, 0x3C7FB1, false);
}
graph.string(strpos.x, strpos.y, style_.fgcolor, name);
if(has_child)
{
nana::paint::gadget::arrow_16_pixels(graph, r.x + r.width - 16, r.y + (r.height - 16)/2,
style_.fgcolor, 3, nana::paint::gadget::directions::to_east);
}
}
void border(graph_reference graph)
{
graph.rectangle(0xF0F0F0, false);
graph.rectangle_line(nana::rectangle(graph.size()).pare_off(1),
0x9DABB9, 0x484E55, 0x484E55, 0x9DABB9);
}
private:
void _m_item_bground(graph_reference graph, int x, int y, unsigned width, unsigned height, mouse_action state)
{
const unsigned half = (height - 2) / 2;
int left = x + 1;
int top = y + 1;
nana::color_t upcol, downcol;
switch(state)
{
case mouse_action::over:
upcol = 0x0DFF2FC;
downcol = 0xA9DAF5;
break;
case mouse_action::pressed:
upcol = 0xA6D7F2;
downcol = 0x92C4F6;
++left;
++top;
break;
case mouse_action::normal:
default:
upcol = 0xEAEAEA;
downcol = 0xDCDCDC;
break;
}
graph.rectangle(left, top, width - 2, half, upcol, true);
graph.rectangle(left, top + static_cast<int>(half), width - 2, (height - 2) - half, downcol, true);
if(mouse_action::pressed == state)
{
int bottom = y + height - 1;
int right = x + width - 1;
graph.line(x, y, right, y, 0x6E8D9F);
graph.line(x, y + 1, x, bottom, 0x6E8D9F);
++x;
++y;
graph.line(x, y, right, y, 0xA6C7D9);
graph.line(x, y + 1, x, bottom, 0xA6C7D9);
}
}
private:
ui_element ui_el_;
struct style_tag
{
nana::color_t bgcolor;
nana::color_t fgcolor;
}style_;
};
class tree_wrapper
{
public:
typedef widgets::detail::tree_cont<item_tag> container;
typedef container::node_type * node_handle;
tree_wrapper()
:splitstr_(STR("\\")), cur_(nullptr)
{}
bool seq(std::size_t index, std::vector<node_handle> & seqv) const
{
_m_read_node_path(seqv);
if(index < seqv.size())
{
if(index)
seqv.erase(seqv.begin(), seqv.begin() + index);
return true;
}
return false;
}
void splitstr(const nana::string& ss)
{
if(ss.size())
splitstr_ = ss;
}
const nana::string& splitstr() const
{
return splitstr_;
}
nana::string path() const
{
std::vector<node_handle> v;
_m_read_node_path(v);
nana::string str;
bool not_head = false;
for(auto i : v)
{
if(not_head)
str += splitstr_;
else
not_head = true;
str += i->value.first;
}
return str;
}
void path(const nana::string& key)
{
cur_ = tree_.ref(key);
}
node_handle at(std::size_t index) const
{
std::vector<node_handle> v;
_m_read_node_path(v);
return (index < v.size() ? v[index] : nullptr);
}
node_handle tail(std::size_t index)
{
node_handle i = at(index);
if(i) cur_ = i;
return i;
}
node_handle cur() const
{
return cur_;
}
void cur(node_handle i)
{
cur_ = i;
}
void insert(const nana::string& name, const nana::any& value)
{
item_tag m;
m.pixels = 0;
m.value = value;
cur_ = tree_.insert(cur_, name, m);
}
bool childset(const nana::string& name, const nana::any& value)
{
if(cur_)
{
item_tag m;
m.pixels = 0;
m.value = value;
tree_.insert(cur_, name, m);
return true;
}
return false;
}
bool childset_erase(const nana::string& name)
{
if(cur_)
{
for(node_handle i = cur_->child; i; i = i->next)
{
if(i->value.first == name)
{
tree_.remove(i);
return true;
}
}
}
return false;
}
node_handle find_child(const nana::string& name) const
{
if(cur_)
{
for(node_handle i = cur_->child; i; i = i->next)
{
if(i->value.first == name)
return i;
}
}
return nullptr;
}
bool clear()
{
if(tree_.get_root()->child)
{
tree_.clear();
return true;
}
return false;
}
private:
void _m_read_node_path(std::vector<node_handle>& v) const
{
node_handle root = tree_.get_root();
for(node_handle i = cur_; i && (i != root); i = i->owner)
v.insert(v.begin(), i);
}
private:
container tree_;
nana::string splitstr_;
node_handle cur_;
};
//class scheme
class trigger::scheme
{
public:
typedef tree_wrapper container;
typedef container::node_handle node_handle;
typedef renderer::ui_element ui_element;
enum class mode
{
normal, floatlist
};
scheme()
: graph_(nullptr)
{
proto_.ui_renderer = pat::cloneable<renderer>(interior_renderer());
style_.mode = mode::normal;
style_.listbox = nullptr;
}
void attach(window wd, nana::paint::graphics* graph)
{
window_ = wd;
API::background(wd, 0xFFFFFF);
graph_ = graph;
}
void detach()
{
window_ = nullptr;
graph_ = nullptr;
}
window window_handle() const
{
return window_;
}
const container& tree() const
{
return treebase_;
}
container& tree()
{
return treebase_;
}
void draw()
{
_m_calc_scale();
nana::rectangle r = _m_make_rectangle(); //_m_make_rectangle must be called after _m_calc_scale()
_m_calc_pixels(r);
proto_.ui_renderer->background(*graph_, window_, r, ui_el_);
if(head_)
proto_.ui_renderer->root_arrow(*graph_, _m_make_root_rectangle(), style_.state);
_m_draw_items(r);
proto_.ui_renderer->border(*graph_);
}
bool locate(int x, int y) const
{
if(graph_)
{
if(head_)
{
auto r = _m_make_root_rectangle();
if (r.is_hit(x, y))
{
style_.active_item_rectangle = r;
if(ui_el_.what == ui_el_.item_root)
return false;
ui_el_.what = ui_el_.item_root;
return true;
}
}
nana::rectangle r = _m_make_rectangle();
std::vector<node_handle> seq;
if(r.is_hit(x, y) && treebase_.seq(head_, seq))
{
const int xbase = r.x;
const int xend = static_cast<int>(r.width) + r.x;
//Change the meaning of variable r. Now, r indicates the area of a item
r.height = item_height_;
std::size_t seq_index = 0;
for(auto i : seq)
{
r.width = i->value.second.pixels;
//If the item is over the right border of widget, the item would be painted at
//the begining of the next line.
if(static_cast<int>(r.width) + r.x > xend)
{
r.x = xbase;
r.y += r.height;
}
if(r.is_hit(x, y))
{
style_.active_item_rectangle = r;
std::size_t index = seq_index + head_;
ui_element::t what = ((i->child && (r.x + static_cast<int>(r.width) - 16 < x))
? ui_el_.item_arrow : ui_el_.item_name);
if(what == ui_el_.what && index == ui_el_.index)
return false;
ui_el_.what = what;
ui_el_.index = index;
return true;
}
r.x += r.width;
++seq_index;
}
}
}
if(ui_el_.what == ui_el_.somewhere) return false;
ui_el_.what = ui_el_.somewhere;
return true;
}
bool erase_locate()
{
ui_el_.index = npos;
if(ui_el_.what != ui_el_.none)
{
ui_el_.what = ui_el_.none;
return true;
}
return false;
}
ui_element locate() const
{
return ui_el_;
}
void mouse_pressed()
{
style_.state = mouse_action::pressed;
switch(ui_el_.what)
{
case ui_element::item_root:
case ui_element::item_arrow:
_m_show_list();
style_.mode = mode::floatlist;
break;
default: //Don't take care about other elements
break;
}
}
void mouse_release()
{
if(style_.mode != mode::floatlist)
{
style_.state = mouse_action::normal;
switch(ui_el_.what)
{
case ui_element::item_name:
_m_selected(treebase_.tail(ui_el_.index));
break;
default: break;
}
}
}
bool is_list_shown() const
{
return (nullptr != style_.listbox);
}
event_agent_holder& evt_holder() const
{
return evt_holder_;
}
private:
void _m_selected(node_handle node)
{
if(node)
{
API::dev::window_caption(window_handle(), tree().path());
if(evt_holder_.selected)
evt_holder_.selected(node->value.second.value);
}
}
void _m_show_list()
{
if(style_.listbox)
style_.listbox->close();
style_.module.items.clear();
nana::rectangle r;
style_.list_trigger = ui_el_.what;
if(ui_el_.what == ui_el_.item_arrow)
{
style_.active = ui_el_.index;
node_handle i = treebase_.at(ui_el_.index);
if(i)
{
for(node_handle child = i->child; child; child = child->next)
style_.module.items.emplace_back(std::make_shared<item>(child->value.first));
}
r = style_.active_item_rectangle;
}
else if(ui_el_.item_root == ui_el_.what)
{
std::vector<node_handle> v;
if(treebase_.seq(0, v))
{
auto end = v.cbegin() + head_;
for(auto i = v.cbegin(); i != end; ++i)
style_.module.items.emplace_back(std::make_shared<item>((*i)->value.first));
}
r = style_.active_item_rectangle;
}
r.y += r.height;
r.width = r.height = 100;
style_.listbox = &(form_loader<nana::float_listbox>()(window_, r, true));
style_.listbox->set_module(style_.module, 16);
style_.listbox->events().destroy.connect([this]
{
_m_list_closed();
});
}
void _m_list_closed()
{
style_.mode = mode::normal;
style_.state = mouse_action::normal;
bool is_draw = false;
if((style_.module.index != npos) && style_.module.have_selected)
{
switch(style_.list_trigger)
{
case ui_element::item_arrow:
{
treebase_.tail(style_.active);
nana::string name = style_.module.items[style_.module.index]->text();
node_handle node = treebase_.find_child(name);
if(node)
{
treebase_.cur(node);
_m_selected(node);
is_draw = true;
}
}
break;
case ui_element::item_root:
_m_selected(treebase_.tail(style_.module.index));
is_draw = true;
break;
default: //Don't take care about other elements
break;
}
}
else
is_draw = true;
if(is_draw)
{
draw();
API::update_window(window_);
}
style_.listbox = nullptr;
}
private:
unsigned _m_item_fix_scale() const
{
return graph_->height() - 2;
}
nana::rectangle _m_make_root_rectangle() const
{
return nana::rectangle(1, 1, 16, _m_item_fix_scale());
}
//_m_make_rectangle
//@brief: This function calculate the items area. This must be called after _m_calc_scale()
nana::rectangle _m_make_rectangle() const
{
nana::rectangle r(1, 1, graph_->width() - 2, _m_item_fix_scale());
unsigned px = r.width;
std::size_t lines = item_lines_;
std::vector<node_handle> v;
treebase_.seq(0, v);
for(auto node : v)
{
if(node->value.second.scale.width > px)
{
if(lines > 1)
{
--lines;
px = r.width;
if(px < node->value.second.scale.width)
{
--lines;
continue;
}
}
else
{
//Too many items, so some of items cann't be displayed
r.x += 16;
r.width -= 16;
return r;
}
}
px -= node->value.second.scale.width;
}
return r;
}
void _m_calc_scale()
{
nana::size tsz;
unsigned highest = 0;
std::vector<node_handle> v;
treebase_.seq(0, v);
for(auto node : v)
{
node->value.second.scale = graph_->text_extent_size(node->value.first);
if(highest < node->value.second.scale.height)
highest = node->value.second.scale.height;
node->value.second.scale.width += (node->child ? 26 : 10);
}
highest += 6; //the default height of item.
item_lines_ = (graph_->height() - 2) / highest;
if(item_lines_ == 0)
item_lines_ = 1;
item_height_ = (1 != item_lines_ ? highest : _m_item_fix_scale());
}
void _m_calc_pixels(const nana::rectangle& r)
{
std::size_t lines = item_lines_;
unsigned px = 0;
head_ = 0;
std::vector<node_handle> v;
treebase_.seq(0, v);
for(auto vi = v.rbegin(); vi != v.rend(); ++vi)
{
item_tag & m = (*vi)->value.second;
if(r.width >= px + m.scale.width)
{
px += m.scale.width;
m.pixels = m.scale.width;
continue;
}
//In fact, this item must be in the font of a line.
m.pixels = (r.width >= m.scale.width ? m.scale.width : _m_minimial_pixels());
if(0 == px) //This line is empty, NOT a newline
{
px = m.pixels;
continue;
}
//Newline, and check here whether is more lines.
if(0 == --lines)
{
head_ = std::distance(vi, v.rend());
break;
}
px = m.pixels;
}
}
unsigned _m_minimial_pixels()
{
return 46;
}
void _m_draw_items(const nana::rectangle& r)
{
nana::rectangle item_r = r;
item_r.height = item_height_;
std::size_t index = head_;
const int xend = static_cast<int>(r.width) + r.x;
std::vector<node_handle> v;
treebase_.seq(0, v);
for(auto vi = v.begin() + head_; vi != v.end(); ++vi)
{
node_handle i = (*vi);
if(static_cast<int>(i->value.second.pixels) + item_r.x > xend)
{
item_r.x = r.x;
item_r.y += item_height_;
}
item_r.width = i->value.second.pixels;
proto_.ui_renderer->item(*graph_, item_r, index++, i->value.first, i->value.second.scale.height, i->child != 0, style_.state);
item_r.x += item_r.width;
}
}
private:
window window_;
nana::paint::graphics * graph_;
nana::string splitstr_;
std::size_t head_;
unsigned item_height_;
std::size_t item_lines_;
container treebase_;
mutable ui_element ui_el_;
struct style_tag
{
ui_element::t list_trigger;
std::size_t active; //It indicates the item corresponding listbox.
mutable ::nana::rectangle active_item_rectangle;
::nana::float_listbox::module_type module;
::nana::float_listbox * listbox;
scheme::mode mode;
mouse_action state; //The state of mouse
}style_;
struct proto_tag
{
pat::cloneable<renderer> ui_renderer;
}proto_;
mutable event_agent_holder evt_holder_;
};
//class trigger
trigger::trigger()
: scheme_(new scheme)
{}
trigger::~trigger()
{
delete scheme_;
}
void trigger::insert(const nana::string& str, nana::any value)
{
scheme_->tree().insert(str, value);
API::dev::window_caption(scheme_->window_handle(), scheme_->tree().path());
scheme_->draw();
}
bool trigger::childset(const nana::string& str, nana::any value)
{
if(scheme_->tree().childset(str, value))
{
scheme_->draw();
return true;
}
return false;
}
bool trigger::childset_erase(const nana::string& str)
{
if(scheme_->tree().childset_erase(str))
{
scheme_->draw();
return true;
}
return false;
}
bool trigger::clear()
{
if(scheme_->tree().clear())
{
scheme_->draw();
return true;
}
return false;
}
void trigger::splitstr(const nana::string& sstr)
{
scheme_->tree().splitstr(sstr);
}
const nana::string& trigger::splitstr() const
{
return scheme_->tree().splitstr();
}
void trigger::path(const nana::string& str)
{
scheme_->tree().path(str);
}
nana::string trigger::path() const
{
return scheme_->tree().path();
}
nana::any& trigger::value() const
{
auto node = scheme_->tree().cur();
if(node)
return node->value.second.value;
throw std::runtime_error("Nana.GUI.categorize::value(), current category is empty");
}
void trigger::_m_event_agent_ready() const
{
auto & evt = scheme_->evt_holder();
auto evt_agent = event_agent_.get();
evt.selected = [evt_agent](::nana::any& val){
evt_agent->selected(val);
};
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
scheme_->attach(widget, &graph);
}
void trigger::detached()
{
scheme_->detach();
}
void trigger::refresh(graph_reference)
{
scheme_->draw();
}
void trigger::mouse_down(graph_reference, const arg_mouse&)
{
if(scheme_->locate().what > ui_element::somewhere)
{
if(API::window_enabled(scheme_->window_handle()))
{
scheme_->mouse_pressed();
scheme_->draw();
API::lazy_refresh();
}
}
}
void trigger::mouse_up(graph_reference, const arg_mouse&)
{
if(scheme_->locate().what > ui_element::somewhere)
{
if(API::window_enabled(scheme_->window_handle()))
{
scheme_->mouse_release();
scheme_->draw();
API::lazy_refresh();
}
}
}
void trigger::mouse_move(graph_reference, const arg_mouse& arg)
{
if(scheme_->locate(arg.pos.x, arg.pos.y) && API::window_enabled(scheme_->window_handle()))
{
scheme_->draw();
API::lazy_refresh();
}
}
void trigger::mouse_leave(graph_reference, const arg_mouse&)
{
if(API::window_enabled(scheme_->window_handle()) && (scheme_->is_list_shown() == false) && scheme_->erase_locate())
{
scheme_->draw();
API::lazy_refresh();
}
}
//end class trigger
}//end namespace categorize
}//end namespace draerbase
}//end namespace nana

View File

@@ -0,0 +1,243 @@
/*
* A CheckBox Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/checkbox.cpp
*/
#include <nana/gui/widgets/checkbox.hpp>
#include <nana/paint/gadget.hpp>
#include <nana/paint/text_renderer.hpp>
#include <nana/gui/element.hpp>
#include <algorithm>
namespace nana{ namespace drawerbase
{
namespace checkbox
{
typedef element::crook_interface::state crook_state;
struct drawer::implement
{
bool react;
bool radio;
facade<element::crook> crook;
};
drawer::drawer()
: widget_(nullptr),
imptr_(new drawer::implement),
impl_(imptr_.get())
{
impl_->react = true;
impl_->radio = false;
}
drawer::~drawer()
{}
void drawer::attached(widget_reference widget, graph_reference)
{
widget_ = &widget;
}
void drawer::refresh(graph_reference graph)
{
_m_draw(graph);
}
void drawer::mouse_down(graph_reference graph, const arg_mouse&)
{
_m_draw(graph);
}
void drawer::mouse_up(graph_reference graph, const arg_mouse&)
{
if(impl_->react)
impl_->crook.reverse();
_m_draw(graph);
}
void drawer::mouse_enter(graph_reference graph, const arg_mouse&)
{
_m_draw(graph);
}
void drawer::mouse_leave(graph_reference graph, const arg_mouse&)
{
_m_draw(graph);
}
drawer::implement * drawer::impl() const
{
return impl_;
}
void drawer::_m_draw(graph_reference graph)
{
_m_draw_background(graph);
_m_draw_title(graph);
_m_draw_checkbox(graph, graph.text_extent_size(STR("jN"), 2).height + 2);
API::lazy_refresh();
}
void drawer::_m_draw_background(graph_reference graph)
{
if(bground_mode::basic != API::effects_bground_mode(*widget_))
graph.rectangle(API::background(*widget_), true);
}
void drawer::_m_draw_checkbox(graph_reference graph, unsigned first_line_height)
{
impl_->crook.draw(graph, widget_->background(), widget_->foreground(), rectangle(0, first_line_height > 16 ? (first_line_height - 16) / 2 : 0, 16, 16), API::element_state(*widget_));
}
void drawer::_m_draw_title(graph_reference graph)
{
if(graph.width() > 16 + interval)
{
nana::string title = widget_->caption();
unsigned fgcolor = widget_->foreground();
unsigned pixels = graph.width() - (16 + interval);
nana::paint::text_renderer tr(graph);
if(API::window_enabled(widget_->handle()) == false)
{
tr.render(17 + interval, 2, 0xFFFFFF, title.c_str(), title.length(), pixels);
fgcolor = 0x808080;
}
tr.render(16 + interval, 1, fgcolor, title.c_str(), title.length(), pixels);
}
}
//end class drawer
} //end namespace checkbox
}//end namespace drawerbase
//class checkbox
checkbox::checkbox(){}
checkbox::checkbox(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
checkbox::checkbox(window wd, const nana::string& text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
checkbox::checkbox(window wd, const nana::char_t* text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
checkbox::checkbox(window wd, const nana::rectangle& r, bool visible)
{
create(wd, r, visible);
}
void checkbox::element_set(const char* name)
{
get_drawer_trigger().impl()->crook.switch_to(name);
}
void checkbox::react(bool want)
{
get_drawer_trigger().impl()->react = want;
}
bool checkbox::checked() const
{
return (get_drawer_trigger().impl()->crook.checked() != drawerbase::checkbox::crook_state::unchecked);
}
void checkbox::check(bool chk)
{
typedef drawerbase::checkbox::crook_state crook_state;
get_drawer_trigger().impl()->crook.check(chk ? crook_state::checked : crook_state::unchecked);
API::refresh_window(handle());
}
void checkbox::radio(bool is_radio)
{
get_drawer_trigger().impl()->crook.radio(is_radio);
}
void checkbox::transparent(bool enabled)
{
if(enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
}
bool checkbox::transparent() const
{
return (bground_mode::basic == API::effects_bground_mode(*this));
}
//end class checkbox
//class radio_group
radio_group::~radio_group()
{
for(auto & i : ui_container_)
{
API::umake_event(i.eh_checked);
API::umake_event(i.eh_destroy);
}
}
void radio_group::add(checkbox& uiobj)
{
uiobj.radio(true);
uiobj.check(false);
uiobj.react(false);
element_tag el;
el.uiobj = &uiobj;
el.eh_checked = uiobj.events().click.connect_front(std::bind(&radio_group::_m_checked, this, std::placeholders::_1));
el.eh_destroy = uiobj.events().destroy.connect(std::bind(&radio_group::_m_destroy, this, std::placeholders::_1));
ui_container_.push_back(el);
}
std::size_t radio_group::checked() const
{
auto i = std::find_if(ui_container_.cbegin(), ui_container_.cend(), [](decltype(*ui_container_.cbegin())& x)
{
return (x.uiobj->checked());
});
return static_cast<std::size_t>(i - ui_container_.cbegin());
}
std::size_t radio_group::size() const
{
return ui_container_.size();
}
void radio_group::_m_checked(const arg_mouse& arg)
{
for (auto & i : ui_container_)
i.uiobj->check(arg.window_handle == i.uiobj->handle());
}
void radio_group::_m_destroy(const arg_destroy& arg)
{
auto i = std::find_if(ui_container_.begin(), ui_container_.end(), [&arg](decltype(*ui_container_.begin()) & x)
{
return (arg.window_handle == x.uiobj->handle());
});
if(i != ui_container_.end())
ui_container_.erase(i);
}
//end class radio_group
}//end namespace nana

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,675 @@
/*
* A date chooser Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/date_chooser.cpp
*/
#include <nana/gui/widgets/date_chooser.hpp>
#include <nana/paint/gadget.hpp>
#include <nana/system/platform.hpp>
#include <sstream>
namespace nana
{
namespace drawerbase
{
namespace date_chooser
{
//class trigger: public drawer_trigger
trigger::trigger()
: widget_(nullptr), chose_(false), page_(page::date), pos_(where::none)
{
const nana::string ws[] = {STR("S"), STR("M"), STR("T"), STR("W"), STR("T"), STR("F"), STR("S")};
const nana::string ms[] = {STR("January"), STR("February"), STR("March"), STR("April"), STR("May"), STR("June"), STR("July"), STR("August"), STR("September"), STR("October"), STR("November"), STR("December")};
for(int i = 0; i < 7; ++i) weekstr_[i] = ws[i];
for(int i = 0; i < 12; ++i) monthstr_[i] = ms[i];
nana::date d;
chdate_.year = chmonth_.year = d.read().year;
chdate_.month = chmonth_.month = d.read().month;
chdate_.day = d.read().day;
}
bool trigger::chose() const
{
return chose_;
}
nana::date trigger::read() const
{
return nana::date(chdate_.year, chdate_.month, chdate_.day);
}
void trigger::week_name(unsigned index, const nana::string& str)
{
if(0 <= index && index < 7)
this->weekstr_[index] = str;
}
void trigger::month_name(unsigned index, const nana::string& str)
{
if(0 <= index && index < 12)
this->monthstr_[index] = str;
}
void trigger::_m_init_color()
{
color_.selected = 0x2F3699;
color_.highlight = 0x4D56C8;
color_.normal = 0x0;
color_.bkcolor = 0x88C4FF;
}
trigger::where trigger::_m_pos_where(graph_reference graph, int x, int y)
{
int xend = static_cast<int>(graph.width()) - 1;
int yend = static_cast<int>(graph.height()) - 1;
if(0 < y && y < static_cast<int>(topbar_height))
{
if(static_cast<int>(border_size) < x && x < xend)
{
if(x < border_size + 16)
return where::left_button;
else if(xend - border_size - 16 < x)
return where::right_button;
return where::topbar;
}
}
else if(topbar_height < y && y < yend)
{
trace_pos_.x = x;
trace_pos_.y = y;
return where::textarea;
}
return where::none;
}
void trigger::_m_draw(graph_reference graph)
{
_m_init_color();
const unsigned width = graph.width() - 2;
graph.rectangle(0xB0B0B0, false);
graph.rectangle(1, 1, width, topbar_height, 0xFFFFFF, true);
_m_draw_topbar(graph);
if(graph.height() > 2 + topbar_height)
{
nana::point refpos(1, static_cast<int>(topbar_height) + 1);
nana::paint::graphics gbuf(width, graph.height() - 2 - topbar_height);
gbuf.rectangle(0xF0F0F0, true);
switch(page_)
{
case page::date:
_m_draw_days(refpos, gbuf);
break;
case page::month:
_m_draw_months(refpos, gbuf);
break;
default: break;
}
graph.bitblt(refpos.x, refpos.y, gbuf);
}
}
void trigger::_m_draw_topbar(graph_reference graph)
{
int ypos = (topbar_height - 16) / 2 + 1;
const nana::color_t color = color_.normal;
nana::paint::gadget::arrow_16_pixels(graph, border_size, ypos, (pos_ == where::left_button ? color_.highlight : color), 1, nana::paint::gadget::directions::to_west);
nana::paint::gadget::arrow_16_pixels(graph, graph.width() - (border_size + 16 + 1), ypos, (pos_ == where::right_button ? color_.highlight : color), 1, nana::paint::gadget::directions::to_east);
if(graph.width() > 32 + border_size * 2)
{
std::stringstream ss;
ss<<chmonth_.year;
nana::string str;
if(page_ == page::date)
{
str += monthstr_[chmonth_.month - 1];
str += STR(" ");
}
str += nana::charset(ss.str());
nana::size txt_s = graph.text_extent_size(str);
ypos = (topbar_height - txt_s.height) / 2 + 1;
int xpos = (graph.width() - txt_s.width) / 2;
if(xpos < border_size + 16) xpos = 16 + border_size + 1;
graph.string(xpos, ypos, (pos_ == where::topbar ? color_.highlight : color), str);
}
}
void trigger::_m_make_drawing_basis(drawing_basis& dbasis, graph_reference graph, const nana::point& refpos)
{
dbasis.refpos = refpos;
const unsigned width = graph.width();
const unsigned height = graph.height();
if(page::date == page_)
{
dbasis.line_s = height / 7.0;
dbasis.row_s = width / 7.0;
}
else if(page::month == page_)
{
dbasis.line_s = height / 3.0;
dbasis.row_s = width / 4.0;
}
dbasis_ = dbasis;
}
void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, const nana::string& str, bool primary, bool sel)
{
nana::rectangle r(static_cast<int>(x * dbasis.row_s), static_cast<int>(y * dbasis.line_s),
static_cast<int>(dbasis.row_s), static_cast<int>(dbasis.line_s));
nana::color_t color{ color_.normal };
nana::point tpos{ trace_pos_ - dbasis.refpos };
if((pos_ == where::textarea)
&& (r.x <= tpos.x)
&& (tpos.x < r.x + static_cast<int>(r.width))
&& (r.y <= tpos.y)
&& (tpos.y < r.y + static_cast<int>(r.height)))
{
if((page_ != page::date) || y)
{
color = color_.highlight;
graph.rectangle(r, color_.bkcolor, true);
}
}
if(sel)
{
color = color_.highlight;
graph.rectangle(r, color_.bkcolor, true);
graph.rectangle(r, color_.selected, false);
}
if(primary == false)
color = 0xB0B0B0;
nana::size txt_s = graph.text_extent_size(str);
graph.string(r.x + static_cast<int>(r.width - txt_s.width) / 2, r.y + static_cast<int>(r.height - txt_s.height) / 2, color, str);
}
void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, int number, bool primary, bool sel)
{
//The C++ library comes with MinGW does not provide std::to_wstring() conversion
std::wstringstream ss;
ss<<number;
_m_draw_pos(dbasis, graph, x, y, nana::charset(ss.str()), primary, sel);
}
void trigger::_m_draw_ex_days(drawing_basis & dbasis, graph_reference graph, int begx, int begy, bool before)
{
int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1);
int y = (x ? 1 : 2);
if(before)
{
int year = chmonth_.year;
int month = chmonth_.month - 1;
if(month == 0)
{
--year;
month = 12;
}
bool same = (chdate_.year == year && chdate_.month == month);
int days = nana::date::month_days(year, month);
int size = (x ? x : 7);
int beg = days - size + 1;
for(int i = 0; i < size; ++i)
{
this->_m_draw_pos(dbasis, graph, i, 1, beg + i, false, same && (chdate_.day == beg + i));
}
}
else
{
int year = chmonth_.year;
int month = chmonth_.month + 1;
if(month == 13)
{
++year;
month = 1;
}
bool same = (chdate_.year == year && chdate_.month == month);
int day = 1;
x = begx;
for(y = begy; y < 7; ++y)
{
for(; x < 7; ++x)
{
_m_draw_pos(dbasis, graph, x, y, day, false, same && (chdate_.day == day));
++day;
}
x = 0;
}
}
}
void trigger::_m_draw_days(const nana::point& refpos, graph_reference graph)
{
drawing_basis dbasis;
_m_make_drawing_basis(dbasis, graph, refpos);
for(int i = 0; i < 7; ++i)
_m_draw_pos(dbasis, graph, i, 0, weekstr_[i], true, false);
int day = 1;
int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1);
int y = (x ? 1 : 2);
//draw the days that before the first day of this month
_m_draw_ex_days(dbasis, graph, 0, 0, true);
//
int days = static_cast<int>(nana::date::month_days(chmonth_.year, chmonth_.month));
bool same = (chdate_.year == chmonth_.year && chdate_.month == chmonth_.month);
while(day <= days)
{
for(; x < 7; ++x)
{
_m_draw_pos(dbasis, graph, x, y, day, true, (same && chdate_.day == day));
if(++day > days) break;
}
if(day > days) break;
y++;
x = 0;
}
++x;
if(x >= 7)
{
x = 0;
++y;
}
_m_draw_ex_days(dbasis, graph, x, y, false);
}
void trigger::_m_draw_months(const nana::point& refpos, graph_reference graph)
{
drawing_basis dbasis;
_m_make_drawing_basis(dbasis, graph, refpos);
for(int y = 0; y < 3; ++y)
for(int x = 0; x < 4; ++x)
{
int index = x + y * 4;
_m_draw_pos(dbasis, graph, x, y, monthstr_[index], true, (chmonth_.year == chdate_.year) && (index + 1 == chdate_.month));
}
}
bool trigger::_m_get_trace(point pos, int & res)
{
pos -= dbasis_.refpos;
int lines = 7, rows = 7; //defaultly for page::date
if(page_ == page::month)
{
lines = 3;
rows = 4;
}
int width = static_cast<int>(dbasis_.row_s * rows);
int height = static_cast<int>(dbasis_.line_s * lines);
if(0 <= pos.x && pos.x < width && 0 <= pos.y && pos.y < height)
{
pos.x = static_cast<int>(pos.x / dbasis_.row_s);
pos.y = static_cast<int>(pos.y / dbasis_.line_s);
int n = pos.y * rows + pos.x + 1;
if(page_ == page::date)
{
if(n < 8) return false; //Here is week title bar
int dw = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1);
n -= (dw ? dw + 7 : 14);
}
res = n;
return true;
}
return false;
}
void trigger::_m_perf_transform(transform_action tfid, graph_reference graph, graph_reference dirtybuf, graph_reference newbuf, const nana::point& refpos)
{
const int sleep_time = 15;
const int count = 20;
double delta = dirtybuf.width() / double(count);
double delta_h = dirtybuf.height() / double(count);
double fade = 1.0 / count;
if(tfid == transform_action::to_right)
{
nana::rectangle dr(0, refpos.y, 0, dirtybuf.height());
nana::rectangle nr(refpos.x, refpos.y, 0, newbuf.height());
for(int i = 1; i < count; ++i)
{
int off_x = static_cast<int>(delta * i);
dr.x = refpos.x + off_x;
dr.width = dirtybuf.width() - off_x;
graph.bitblt(dr, dirtybuf);
nr.width = off_x;
graph.bitblt(nr, newbuf, nana::point(static_cast<int>(dr.width), 0));
API::update_window(*widget_);
nana::system::sleep(sleep_time);
}
}
else if(tfid == transform_action::to_left)
{
double delta = dirtybuf.width() / double(count);
nana::rectangle dr(refpos.x, refpos.y, 0, dirtybuf.height());
nana::rectangle nr(0, refpos.y, 0, newbuf.height());
for(int i = 1; i < count; ++i)
{
int off_x = static_cast<int>(delta * i);
dr.width = dirtybuf.width() - off_x;
graph.bitblt(dr, dirtybuf, nana::point(off_x, 0));
nr.x = refpos.x + static_cast<int>(dr.width);
nr.width = off_x;
graph.bitblt(nr, newbuf);
API::update_window(*widget_);
nana::system::sleep(sleep_time);
}
}
else if(tfid == transform_action::to_leave)
{
nana::paint::graphics dzbuf(newbuf.size());
nana::paint::graphics nzbuf(newbuf.size());
nana::rectangle r;
for(int i = 1; i < count; ++i)
{
r.width = static_cast<int>(newbuf.width() - delta * i);
r.height = static_cast<int>(newbuf.height() - delta_h * i);
r.x = static_cast<int>(newbuf.width() - r.width) / 2;
r.y = static_cast<int>(newbuf.height() - r.height) / 2;
dzbuf.rectangle(0xFFFFFF, true);
dirtybuf.stretch(dzbuf, r);
r.width = static_cast<int>(newbuf.width() + delta * (count - i));
r.height = static_cast<int>(newbuf.height() + delta_h * (count - i));
r.x = static_cast<int>(newbuf.width() - r.width) / 2;
r.y = static_cast<int>(newbuf.height() - r.height) / 2;
newbuf.stretch(nzbuf, r);
nzbuf.blend(nzbuf.size(), dzbuf, nana::point(), fade * (count - i));
graph.bitblt(refpos.x, refpos.y, dzbuf);
API::update_window(*widget_);
nana::system::sleep(sleep_time);
}
}
else if(tfid == transform_action::to_enter)
{
nana::paint::graphics dzbuf(newbuf.size());
nana::paint::graphics nzbuf(newbuf.size());
nana::rectangle r;
for(int i = 1; i < count; ++i)
{
r.width = static_cast<int>(newbuf.width() + delta * i);
r.height = static_cast<int>(newbuf.height() + delta_h * i);
r.x = static_cast<int>(newbuf.width() - r.width) / 2;
r.y = static_cast<int>(newbuf.height() - r.height) / 2;
dirtybuf.stretch(dzbuf, r);
r.width = static_cast<int>(newbuf.width() - delta * (count - i));
r.height = static_cast<int>(newbuf.height() - delta_h * (count - i));
r.x = static_cast<int>(newbuf.width() - r.width) / 2;
r.y = static_cast<int>(newbuf.height() - r.height) / 2;
nzbuf.rectangle(0xFFFFFF, true);
newbuf.stretch(nzbuf, r);
nzbuf.blend(nzbuf.size(), dzbuf, nana::point(), fade * (count - i));
graph.bitblt(refpos.x, refpos.y, dzbuf);
API::update_window(*widget_);
nana::system::sleep(sleep_time);
}
}
graph.bitblt(nana::rectangle(refpos, newbuf.size()), newbuf);
}
void trigger::refresh(graph_reference graph)
{
_m_draw(graph);
}
void trigger::attached(widget_reference widget, graph_reference)
{
widget_ = &widget;
}
void trigger::mouse_move(graph_reference graph, const arg_mouse& arg)
{
where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y);
if(pos == pos_ && pos_ != where::textarea) return;
pos_ = pos;
_m_draw(graph);
API::lazy_refresh();
}
void trigger::mouse_leave(graph_reference graph, const arg_mouse&)
{
if(where::none == pos_) return;
pos_ = where::none;
_m_draw(graph);
API::lazy_refresh();
}
void trigger::mouse_up(graph_reference graph, const arg_mouse& arg)
{
bool redraw = true;
where pos = _m_pos_where(graph, arg.pos.x, arg.pos.y);
transform_action tfid = transform_action::none;
if(pos == where::topbar)
{
switch(page_)
{
case page::date:
page_ = page::month;
tfid = transform_action::to_leave;
break;
default:
redraw = false;
}
}
else if(pos == where::textarea)
{
int ret = 0;
switch(page_)
{
case page::date:
if(_m_get_trace(arg.pos, ret))
{
if(ret < 1)
{
if(--chmonth_.month == 0)
{
--chmonth_.year;
chmonth_.month = 12;
}
tfid = transform_action::to_right;
}
else
{
int days = nana::date::month_days(chmonth_.year, chmonth_.month);
if(ret > days)
{
if(++chmonth_.month == 13)
{
++chmonth_.year;
chmonth_.month = 1;
}
tfid = transform_action::to_left;
}
else //Selecting a day in this month
{
chdate_.year = chmonth_.year;
chdate_.month = chmonth_.month;
chdate_.day = ret;
chose_ = true;
}
}
}
break;
case page::month:
if(_m_get_trace(arg.pos, ret))
chmonth_.month = ret;
page_ = page::date;
tfid = transform_action::to_enter;
break;
default:
redraw = false;
}
}
else if(pos == where::left_button || pos == where::right_button)
{
int end_m;
int beg_m;
int step;
if(pos == where::left_button)
{
end_m = 1;
beg_m = 12;
step = -1;
tfid = transform_action::to_right;
}
else
{
end_m = 12;
beg_m = 1;
step = 1;
tfid = transform_action::to_left;
}
switch(page_)
{
case page::date:
if(chmonth_.month == end_m)
{
chmonth_.month = beg_m;
chmonth_.year += step;
}
else
chmonth_.month += step;
break;
case page::month:
chmonth_.year += step;
break;
default:
redraw = false;
}
}
if(redraw)
{
if(tfid != transform_action::none)
{
nana::point refpos(1, static_cast<int>(topbar_height) + 1);
nana::rectangle r(0, 0, graph.width() - 2, graph.height() - 2 - topbar_height);
nana::paint::graphics dirtybuf(r.width, r.height);
dirtybuf.bitblt(r, graph, refpos);
_m_draw(graph);
nana::paint::graphics gbuf(r.width, r.height);
gbuf.bitblt(r, graph, refpos);
_m_perf_transform(tfid, graph, dirtybuf, gbuf, refpos);
}
else
_m_draw(graph);
API::lazy_refresh();
}
}
//end class trigger
}//end namespace date_chooser
}//end namespace drawerbase
//class date_chooser
date_chooser::date_chooser()
{}
date_chooser::date_chooser(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
date_chooser::date_chooser(window wd, const nana::string& text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
date_chooser::date_chooser(window wd, const nana::char_t* text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
date_chooser::date_chooser(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
bool date_chooser::chose() const
{
return get_drawer_trigger().chose();
}
nana::date date_chooser::read() const
{
return get_drawer_trigger().read();
}
void date_chooser::weekstr(unsigned index, const nana::string& str)
{
get_drawer_trigger().week_name(index, str);
API::refresh_window(*this);
}
void date_chooser::monthstr(unsigned index, const nana::string& str)
{
get_drawer_trigger().month_name(index, str);
API::refresh_window(*this);
}
//end class date_chooser
}//end namespace nana

View File

@@ -0,0 +1,520 @@
/*
* A float_listbox Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/float_listbox.cpp
*/
#include <nana/gui/widgets/float_listbox.hpp>
#include <nana/gui/widgets/scroll.hpp>
namespace nana
{
namespace drawerbase{
namespace float_listbox
{
//class item_renderer
item_renderer::~item_renderer(){}
//end class item_renderer
class def_item_renderer
: public item_renderer
{
bool image_enabled_;
unsigned image_pixels_;
void image(bool enb, unsigned px)
{
image_enabled_ = enb;
image_pixels_ = px;
}
void render(widget_reference, graph_reference graph, const nana::rectangle& r, const item_interface* item, state_t state)
{
if(state == StateHighlighted)
{
graph.rectangle(r, 0xAFC7E3, false);
graph.set_pixel(r.x, r.y, 0xFFFFFF);
graph.set_pixel(r.x + r.width - 1, r.y, 0xFFFFFF);
graph.set_pixel(r.x, r.y + r.height - 1, 0xFFFFFF);
graph.set_pixel(r.x + r.width - 1, r.y + r.height - 1, 0xFFFFFF);
graph.set_pixel(r.x + 1, r.y + 1, 0xAFC7E3);
graph.set_pixel(r.x + r.width - 2, r.y + 1, 0xAFC7E3);
graph.set_pixel(r.x + 1, r.y + r.height - 2, 0xAFC7E3);
graph.set_pixel(r.x + r.width - 2, r.y + r.height - 2, 0xAFC7E3);
nana::rectangle po_r(r);
graph.rectangle(po_r.pare_off(1), 0xEBF4FB, false);
graph.shadow_rectangle(po_r.pare_off(1), 0xDDECFD, 0xC2DCFD, true);
}
else
graph.rectangle(r, 0xFFFFFF, true);
int x = r.x + 2;
if(image_enabled_)
{
unsigned vpix = (r.height - 4);
if(item->image())
{
nana::size imgsz = item->image().size();
if(imgsz.width > image_pixels_)
{
unsigned new_h = image_pixels_ * imgsz.height / imgsz.width;
if(new_h > vpix)
{
imgsz.width = vpix * imgsz.width / imgsz.height;
imgsz.height = vpix;
}
else
{
imgsz.width = image_pixels_;
imgsz.height = new_h;
}
}
else if(imgsz.height > vpix)
{
unsigned new_w = vpix * imgsz.width / imgsz.height;
if(new_w > image_pixels_)
{
imgsz.height = image_pixels_ * imgsz.height / imgsz.width;
imgsz.width = image_pixels_;
}
else
{
imgsz.height = vpix;
imgsz.width = new_w;
}
}
nana::point to_pos(x, r.y + 2);
to_pos.x += (image_pixels_ - imgsz.width) / 2;
to_pos.y += (vpix - imgsz.height) / 2;
item->image().stretch(item->image().size(), graph, nana::rectangle(to_pos, imgsz));
}
x += (image_pixels_ + 2);
}
graph.string(x, r.y + 2, 0x0, item->text());
}
unsigned item_pixels(graph_reference graph) const
{
return graph.text_extent_size(STR("jHWn/?\\{[(0569")).height + 4;
}
};//end class item_renderer
//struct module_def
module_def::module_def()
:max_items(10), index(npos)
{}
//end struct module_def
//class drawer_impl
class drawer_impl
{
public:
typedef widget& widget_reference;
typedef nana::paint::graphics& graph_reference;
drawer_impl()
: widget_(nullptr), graph_(nullptr), image_pixels_(16),
ignore_first_mouseup_(true), module_(nullptr)
{}
void clear_state()
{
state_.offset_y = 0;
state_.index = npos;
}
void ignore_first_mouse_up(bool value)
{
ignore_first_mouseup_ = value;
}
bool ignore_emitting_mouseup()
{
if(ignore_first_mouseup_)
{
ignore_first_mouseup_ = false;
return true;
}
return false;
}
void renderer(item_renderer* ir)
{
state_.renderer = (ir ? ir : state_.orig_renderer);
}
void scroll_items(bool upwards)
{
if(scrollbar_.empty()) return;
bool update = false;
if(upwards)
{
if(state_.offset_y)
{
--(state_.offset_y);
update = true;
}
}
else
{
if((state_.offset_y + module_->max_items) < module_->items.size())
{
++(state_.offset_y);
update = true;
}
}
if(update)
{
draw();
scrollbar_.value(state_.offset_y);
API::update_window(*widget_);
}
}
void move_items(bool upwards, bool recycle)
{
if(module_ && module_->items.size())
{
std::size_t init_index = state_.index;
if(state_.index != npos)
{
unsigned last_offset_y = 0;
if(module_->items.size() > module_->max_items)
last_offset_y = static_cast<unsigned>(module_->items.size() - module_->max_items);
if(upwards)
{
if(state_.index)
--(state_.index);
else if(recycle)
{
state_.index = static_cast<unsigned>(module_->items.size() - 1);
state_.offset_y = last_offset_y;
}
if(state_.index < state_.offset_y)
state_.offset_y = state_.index;
}
else
{
if(state_.index < module_->items.size() - 1)
++(state_.index);
else if(recycle)
{
state_.index = 0;
state_.offset_y = 0;
}
if(state_.index >= state_.offset_y + module_->max_items)
state_.offset_y = static_cast<unsigned>(state_.index - module_->max_items + 1);
}
}
else
state_.index = 0;
if(init_index != state_.index)
{
draw();
scrollbar_.value(state_.offset_y);
API::update_window(*widget_);
}
}
}
std::size_t index() const
{
return state_.index;
}
widget* widget_ptr()
{
return widget_;
}
void attach(widget* wd, nana::paint::graphics* graph)
{
if(wd)
{
widget_ = wd;
wd->events().mouse_wheel.connect([this](const arg_wheel& arg){
scroll_items(arg.upwards);
});
}
if(graph) graph_ = graph;
}
void detach()
{
graph_ = nullptr;
}
void resize()
{
if(module_)
{
std::size_t items = (module_->max_items <= module_->items.size() ? module_->max_items : module_->items.size());
std::size_t h = items * state_.renderer->item_pixels(*graph_);
widget_->size(size{ widget_->size().width, static_cast<unsigned>(h + 4) });
}
}
void set_module(const module_def& md, unsigned pixels)
{
module_ = &md;
md.have_selected = false;
if(md.index >= md.items.size())
md.index = npos;
image_pixels_ = pixels;
}
void set_result()
{
if(module_)
{
module_->index = state_.index;
module_->have_selected = true;
}
}
bool right_area(graph_reference graph, int x, int y) const
{
return ((1 < x && 1 < y) &&
x < static_cast<int>(graph.width()) - 2 &&
y < static_cast<int>(graph.height()) - 2);
}
bool set_mouse(graph_reference graph, int x, int y)
{
if(this->right_area(graph, x, y))
{
const unsigned n = (y - 2) / state_.renderer->item_pixels(graph) + static_cast<unsigned>(state_.offset_y);
if(n != state_.index)
{
state_.index = n;
return true;
}
}
return false;
}
void draw()
{
if(module_)
{
bool pages = (module_->max_items < module_->items.size());
const unsigned outter_w = (pages ? 20 : 4);
if(graph_->width() > outter_w && graph_->height() > 4 )
{
//Draw items
std::size_t items = (pages ? module_->max_items : module_->items.size());
items += state_.offset_y;
const unsigned item_pixels = state_.renderer->item_pixels(*graph_);
nana::rectangle item_r(2, 2, graph_->width() - outter_w, item_pixels);
state_.renderer->image(_m_image_enabled(), image_pixels_);
for(std::size_t i = state_.offset_y; i < items; ++i)
{
item_renderer::state_t state = item_renderer::StateNone;
if(i == state_.index) state = item_renderer::StateHighlighted;
state_.renderer->render(*widget_, *graph_, item_r, module_->items[i].get(), state);
item_r.y += item_pixels;
}
}
_m_open_scrollbar(*widget_, pages);
}
else
graph_->string(4, 4, 0x808080, STR("Empty Listbox, No Module!"));
//Draw border
graph_->rectangle(0x0, false);
graph_->rectangle(nana::rectangle(graph_->size()).pare_off(1), 0xFFFFFF, false);
}
private:
bool _m_image_enabled() const
{
for(auto & i : module_->items)
{
if(false == i->image().empty())
return true;
}
return false;
}
void _m_open_scrollbar(widget_reference wd, bool v)
{
if(v)
{
if(scrollbar_.empty() && module_)
{
scrollbar_.create(wd, rectangle(static_cast<int>(wd.size().width - 18), 2, 16, wd.size().height - 4));
scrollbar_.amount(module_->items.size());
scrollbar_.range(module_->max_items);
scrollbar_.value(state_.offset_y);
auto & events = scrollbar_.events();
events.mouse_wheel.connect([this](const arg_wheel& arg)
{
scroll_items(arg.upwards);
});
auto fn = [this](const arg_mouse& arg)
{
if (arg.left_button && (scrollbar_.value() != state_.offset_y))
{
state_.offset_y = static_cast<unsigned>(scrollbar_.value());
draw();
API::update_window(*widget_);
}
};
events.mouse_move.connect(fn);
events.mouse_up.connect(fn);
}
}
else
scrollbar_.close();
}
private:
widget * widget_;
nana::paint::graphics * graph_;
unsigned image_pixels_; //Define the width pixels of the image area
bool ignore_first_mouseup_;
struct state_type
{
std::size_t offset_y;
std::size_t index; //The index of the selected item.
item_renderer * const orig_renderer;
item_renderer * renderer;
state_type(): offset_y(0), index(npos), orig_renderer(new def_item_renderer), renderer(orig_renderer){}
~state_type()
{
delete orig_renderer;
}
}state_;
nana::scroll<true> scrollbar_;
const module_def* module_;
};
//class drawer_impl;
//class trigger
trigger::trigger()
:drawer_(new drawer_impl)
{}
trigger::~trigger()
{
delete drawer_;
}
drawer_impl& trigger::get_drawer_impl()
{
return *drawer_;
}
const drawer_impl& trigger::get_drawer_impl() const
{
return *drawer_;
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
drawer_->attach(&widget, &graph);
}
void trigger::detached()
{
drawer_->detach();
}
void trigger::refresh(graph_reference)
{
drawer_->draw();
}
void trigger::mouse_move(graph_reference graph, const arg_mouse& arg)
{
if(drawer_->set_mouse(graph, arg.pos.x, arg.pos.y))
{
drawer_->draw();
API::lazy_refresh();
}
}
void trigger::mouse_up(graph_reference graph, const arg_mouse& arg)
{
if(drawer_->right_area(graph, arg.pos.x, arg.pos.y))
{
drawer_->set_result();
drawer_->widget_ptr()->close();
}
else if(false == drawer_->ignore_emitting_mouseup())
drawer_->widget_ptr()->close();
}
//end class trigger
}
}//end namespace drawerbase
//class float_listbox
float_listbox::float_listbox(window wd, const rectangle & r, bool is_ignore_first_mouse_up)
:base_type(wd, false, r, appear::bald<appear::floating, appear::no_activate>())
{
API::capture_window(handle(), true);
API::capture_ignore_children(false);
API::take_active(handle(), false, parent());
auto & impl = get_drawer_trigger().get_drawer_impl();
impl.clear_state();
impl.ignore_first_mouse_up(is_ignore_first_mouse_up);
}
void float_listbox::set_module(const float_listbox::module_type& md, unsigned pixels)
{
auto & impl = get_drawer_trigger().get_drawer_impl();
impl.set_module(md, pixels);
impl.resize();
show();
}
void float_listbox::scroll_items(bool upwards)
{
get_drawer_trigger().get_drawer_impl().scroll_items(upwards);
}
void float_listbox::move_items(bool upwards, bool circle)
{
get_drawer_trigger().get_drawer_impl().move_items(upwards, circle);
}
void float_listbox::renderer(item_renderer* ir)
{
auto & impl = get_drawer_trigger().get_drawer_impl();
impl.renderer(ir);
impl.resize();
}
std::size_t float_listbox::index() const
{
return get_drawer_trigger().get_drawer_impl().index();
}
//end class float_listbox
}

View File

@@ -0,0 +1,81 @@
/*
* A Form Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/form.cpp
*/
#include <nana/gui/widgets/form.hpp>
namespace nana
{
namespace drawerbase
{
namespace form
{
//class trigger
trigger::trigger():wd_(nullptr){}
void trigger::attached(widget_reference widget, graph_reference graph)
{
wd_ = &widget;
}
void trigger::refresh(graph_reference graph)
{
graph.rectangle(API::background(*wd_), true);
}
void trigger::resized(graph_reference graph, const arg_resized&)
{
graph.rectangle(API::background(*wd_), true);
API::lazy_refresh();
}
}//end namespace form
}//end namespace drawerbase
//class form
typedef widget_object<category::root_tag, drawerbase::form::trigger, ::nana::detail::events_root_extension> form_base_t;
form::form(const form& fm, const ::nana::size& sz, const appearance& apr)
: form_base_t(fm.handle(), false, API::make_center(fm.handle(), sz.width, sz.height), apr)
{
}
form::form(const rectangle& r, const appearance& apr)
: form_base_t(nullptr, false, r, apr)
{}
form::form(window owner, const ::nana::size& sz, const appearance& apr)
: form_base_t(owner, false, API::make_center(owner, sz.width, sz.height), apr)
{}
form::form(window owner, const rectangle& r, const appearance& apr)
: form_base_t(owner, false, r, apr)
{}
//end class form
//class nested_form
nested_form::nested_form(const form& fm, const rectangle& r, const appearance& apr)
: form_base_t(fm.handle(), true, r, apr)
{
}
nested_form::nested_form(const nested_form& fm, const rectangle& r, const appearance& apr)
: form_base_t(fm.handle(), true, r, apr)
{
}
nested_form::nested_form(window owner, const appearance& apr)
: form_base_t(owner, true, rectangle(), apr)
{}
nested_form::nested_form(window owner, const rectangle& r, const appearance& apr)
: form_base_t(owner, true, r, apr)
{}
//end nested_form
}//end namespace nana

View File

@@ -0,0 +1,47 @@
/*
* A Frame Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/frame.cpp
*
* A frame provides a way to contain the platform window in a stdex GUI Window
*/
#include <nana/gui/widgets/frame.hpp>
namespace nana
{
//class frame:: public widget_object<category::frame_tag>
frame::frame(){}
frame::frame(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
frame::frame(window wd, const nana::rectangle& r, bool visible)
{
create(wd, r, visible);
}
bool frame::insert(native_window_type wd)
{
return API::insert_frame(handle(), wd);
}
native_window_type frame::element(unsigned index)
{
return API::frame_element(handle(), index);
}
native_window_type frame::container() const
{
return API::frame_container(handle());
}
//end class frame
}//end namespace nana

View File

@@ -0,0 +1,872 @@
/*
* A Label Control Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: source/gui/widgets/label.cpp
*/
#include <nana/gui/widgets/label.hpp>
#include <nana/unicode_bidi.hpp>
#include <nana/gui/widgets/skeletons/text_token_stream.hpp>
#include <nana/system/platform.hpp>
#include <stdexcept>
#include <sstream>
namespace nana
{
namespace drawerbase
{
namespace label
{
class renderer
{
typedef widgets::skeletons::dstream::linecontainer::iterator iterator;
struct pixel_tag
{
int x_base; //The x position where this line starts.
std::size_t pixels;
std::size_t baseline; //The baseline for drawing text.
std::vector<iterator> values; //line values
};
//this is a helper variable, it just keeps the status while drawing.
struct render_status
{
unsigned allowed_width;
align text_align;
align_v text_align_v;
nana::point pos;
std::vector<pixel_tag> pixels;
std::size_t index;
};
struct traceable
{
nana::rectangle r;
nana::string target;
nana::string url;
};
public:
typedef nana::paint::graphics& graph_reference;
typedef widgets::skeletons::dstream dstream;
typedef widgets::skeletons::fblock fblock;
typedef widgets::skeletons::data data;
void parse(const nana::string& s)
{
dstream_.parse(s, format_enabled_);
}
bool format(bool fm)
{
if (fm == format_enabled_)
return false;
format_enabled_ = fm;
return true;
}
void render(graph_reference graph, nana::color_t fgcolor, align th, align_v tv)
{
traceable_.clear();
nana::paint::font ft = graph.typeface(); //used for restoring the font
const unsigned def_line_pixels = graph.text_extent_size(STR(" "), 1).height;
font_ = ft;
fblock_ = nullptr;
_m_set_default(ft, fgcolor);
_m_measure(graph);
render_status rs;
rs.allowed_width = graph.size().width;
rs.text_align = th;
rs.text_align_v = tv;
std::deque<std::vector<pixel_tag> > pixel_lines;
std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted.
for (auto & line : dstream_)
{
_m_line_pixels(line, def_line_pixels, rs);
for (auto & m : rs.pixels)
extent_v_pixels += m.pixels;
pixel_lines.emplace_back(std::move(rs.pixels));
if(extent_v_pixels >= graph.height())
break;
}
if((tv != align_v::top) && extent_v_pixels < graph.height())
{
if(align_v::center == tv)
rs.pos.y = static_cast<int>(graph.height() - extent_v_pixels) >> 1;
else if(align_v::bottom == tv)
rs.pos.y = static_cast<int>(graph.height() - extent_v_pixels);
}
else
rs.pos.y = 0;
auto pixels_iterator = pixel_lines.begin();
for (auto & line : dstream_)
{
if (rs.pos.y >= static_cast<int>(graph.height()))
break;
rs.index = 0;
rs.pixels.clear();
rs.pixels.swap(*pixels_iterator++);
rs.pos.x = rs.pixels.front().x_base;
//Stop drawing when it goes out of range.
if(false == _m_each_line(graph, line, rs))
break;
rs.pos.y += static_cast<int>(rs.pixels.back().pixels);
}
graph.typeface(ft);
}
bool find(int x, int y, nana::string& target, nana::string& url) const
{
for (auto & t : traceable_)
{
if(t.r.is_hit(x, y))
{
target = t.target;
url = t.url;
return true;
}
}
return false;
}
nana::size measure(graph_reference graph, unsigned limited, align th, align_v tv)
{
nana::size retsize;
nana::paint::font ft = graph.typeface(); //used for restoring the font
const unsigned def_line_pixels = graph.text_extent_size(STR(" "), 1).height;
font_ = ft;
fblock_ = nullptr;
_m_set_default(ft, 0);
_m_measure(graph);
render_status rs;
rs.allowed_width = limited;
rs.text_align = th;
rs.text_align_v = tv;
for(auto i = dstream_.begin(), end = dstream_.end(); i != end; ++i)
{
rs.pixels.clear();
unsigned w = _m_line_pixels(*i, def_line_pixels, rs);
if(limited && (w > limited))
w = limited;
if(retsize.width < w)
retsize.width = w;
for (auto & px : rs.pixels)
retsize.height += static_cast<unsigned>(px.pixels);
}
return retsize;
}
private:
//Manage the fblock for a specified rectangle if it is a traceable fblock.
void _m_inser_if_traceable(int x, int y, const nana::size& sz, widgets::skeletons::fblock* fbp)
{
if(fbp->target.size() || fbp->url.size())
{
traceable tr;
tr.r.x = x;
tr.r.y = y;
tr.r.width = sz.width;
tr.r.height = sz.height;
tr.target = fbp->target;
tr.url = fbp->url;
traceable_.push_back(tr);
}
}
void _m_set_default(const nana::paint::font& ft, nana::color_t fgcolor)
{
def_.font_name = ft.name();
def_.font_size = ft.size();
def_.font_bold = ft.bold();
def_.fgcolor = fgcolor;
}
nana::color_t _m_fgcolor(nana::widgets::skeletons::fblock* fp)
{
while(fp->fgcolor == 0xFFFFFFFF)
{
fp = fp->parent;
if(nullptr == fp)
return def_.fgcolor;
}
return fp->fgcolor;
}
std::size_t _m_font_size(nana::widgets::skeletons::fblock* fp)
{
while(fp->font_size == 0xFFFFFFFF)
{
fp = fp->parent;
if(nullptr == fp)
return def_.font_size;
}
return fp->font_size;
}
bool _m_bold(nana::widgets::skeletons::fblock* fp)
{
while(fp->bold_empty)
{
fp = fp->parent;
if(nullptr == fp)
return def_.font_bold;
}
return fp->bold;
}
const nana::string& _m_fontname(nana::widgets::skeletons::fblock* fp)
{
while(fp->font.empty())
{
fp = fp->parent;
if(nullptr == fp)
return def_.font_name;
}
return fp->font;
}
void _m_change_font(graph_reference graph, nana::widgets::skeletons::fblock* fp)
{
if(fp != fblock_)
{
const nana::string& name = _m_fontname(fp);
auto fontsize = static_cast<unsigned>(_m_font_size(fp));
bool bold = _m_bold(fp);
if((fontsize != font_.size()) || bold != font_.bold() || name != font_.name())
{
font_.make(name.data(), fontsize, bold);
graph.typeface(font_);
}
fblock_ = fp;
}
}
void _m_measure(graph_reference graph)
{
nana::paint::font ft = font_;
for (auto & line : dstream_)
{
for (auto & value : line)
{
_m_change_font(graph, value.fblock_ptr);
value.data_ptr->measure(graph);
}
}
if(font_ != ft)
{
font_ = ft;
graph.typeface(ft);
fblock_ = nullptr;
}
}
void _m_align_x_base(const render_status& rs, pixel_tag & px, unsigned w)
{
switch(rs.text_align)
{
case align::left:
px.x_base = 0;
break;
case align::center:
px.x_base = (static_cast<int>(rs.allowed_width - w) >> 1);
break;
case align::right:
px.x_base = static_cast<int>(rs.allowed_width - w);
break;
}
}
unsigned _m_line_pixels(dstream::linecontainer& line, unsigned def_line_pixels, render_status & rs)
{
if (line.empty())
{
pixel_tag px;
px.baseline = 0;
px.pixels = def_line_pixels;
px.x_base = 0;
rs.pixels.push_back(px);
return 0;
}
unsigned total_w = 0;
unsigned w = 0;
unsigned max_ascent = 0;
unsigned max_descent = 0;
unsigned max_px = 0;
//Bidi reorder is requried here
std::vector<iterator> line_values;
for(auto i = line.begin(), end = line.end(); i != end; ++i)
{
data * data_ptr = i->data_ptr;
nana::size sz = data_ptr->size();
total_w += sz.width;
unsigned as = 0; //ascent
unsigned ds = 0; //descent
if(fblock::aligns::baseline == i->fblock_ptr->text_align)
{
as = static_cast<unsigned>(data_ptr->ascent());
ds = static_cast<unsigned>(sz.height - as);
if(max_descent < ds)
max_descent = ds;
if((false == data_ptr->is_text()) && (sz.height < max_ascent + max_descent))
sz.height = max_ascent + max_descent;
}
if(w + sz.width <= rs.allowed_width)
{
w += sz.width;
if(max_ascent < as) max_ascent = as;
if(max_descent < ds) max_descent = ds;
if(max_px < sz.height) max_px = sz.height;
line_values.push_back(i);
}
else
{
if(w)
{
pixel_tag px;
_m_align_x_base(rs, px, w);
if(max_ascent + max_descent > max_px)
max_px = max_descent + max_ascent;
else
max_ascent = max_px - max_descent;
px.pixels = max_px;
px.baseline = max_ascent;
px.values.swap(line_values);
rs.pixels.push_back(px);
w = sz.width;
max_px = sz.height;
max_ascent = as;
max_descent = ds;
line_values.push_back(i);
}
else
{
pixel_tag px;
_m_align_x_base(rs, px, sz.width);
px.pixels = sz.height;
px.baseline = as;
px.values.push_back(i);
rs.pixels.push_back(px);
max_px = 0;
max_ascent = max_descent = 0;
}
}
}
if (max_px)
{
pixel_tag px;
_m_align_x_base(rs, px, w);
if (max_ascent + max_descent > max_px)
max_px = max_descent + max_ascent;
else
max_ascent = max_px - max_descent;
px.pixels = max_px;
px.baseline = max_ascent;
px.values.swap(line_values);
rs.pixels.push_back(px);
}
return total_w;
}
bool _m_each_line(graph_reference graph, dstream::linecontainer& line, render_status& rs)
{
nana::string text;
iterator block_start;
const int lastpos = static_cast<int>(graph.height()) - 1;
for(auto i = rs.pixels.begin(), end = rs.pixels.end(); i != end; ++i)
{
for (auto & render_iterator : i->values)
{
auto & value = *render_iterator;
if(false == value.data_ptr->is_text())
{
if(text.size())
{
_m_draw_block(graph, text, block_start, rs);
if(lastpos <= rs.pos.y)
return false;
text.clear();
}
nana::size sz = value.data_ptr->size();
pixel_tag px = rs.pixels[rs.index];
if ((rs.allowed_width < rs.pos.x + sz.width) && (rs.pos.x != px.x_base))
{
//Change a line.
rs.pos.y += static_cast<int>(px.pixels);
px = rs.pixels[++rs.index];
rs.pos.x = px.x_base;
}
int y = rs.pos.y + _m_text_top(px, value.fblock_ptr, value.data_ptr);
value.data_ptr->nontext_render(graph, rs.pos.x, y);
_m_inser_if_traceable(rs.pos.x, y, sz, value.fblock_ptr);
rs.pos.x += static_cast<int>(sz.width);
if(lastpos < y)
return false;
}
else
{
//hold the block while the text is empty,
//it stands for the first block
if(text.empty())
block_start = render_iterator;
text += value.data_ptr->text();
}
}
if(text.size())
{
_m_draw_block(graph, text, block_start, rs);
text.clear();
}
}
return (rs.pos.y <= lastpos);
}
static bool _m_overline(const render_status& rs, int right, bool equal_required)
{
if(align::left == rs.text_align)
return (equal_required ? right >= static_cast<int>(rs.allowed_width) : right > static_cast<int>(rs.allowed_width));
return (equal_required ? rs.pixels[rs.index].x_base <= 0 : rs.pixels[rs.index].x_base < 0);
}
static int _m_text_top(const pixel_tag& px, fblock* fblock_ptr, const data* data_ptr)
{
switch(fblock_ptr->text_align)
{
case fblock::aligns::center:
return static_cast<int>(px.pixels - data_ptr->size().height) / 2;
case fblock::aligns::bottom:
return static_cast<int>(px.pixels - data_ptr->size().height);
case fblock::aligns::baseline:
return static_cast<int>(px.baseline - (data_ptr->is_text() ? data_ptr->ascent() : data_ptr->size().height));
default: break;
}
return 0;
}
void _m_draw_block(graph_reference graph, const nana::string& s, dstream::linecontainer::iterator block_start, render_status& rs)
{
nana::unicode_bidi bidi;
std::vector<nana::unicode_bidi::entity> reordered;
bidi.linestr(s.data(), s.length(), reordered);
pixel_tag px = rs.pixels[rs.index];
for(auto & bidi : reordered)
{
std::size_t pos = bidi.begin - s.data();
std::size_t len = bidi.end - bidi.begin;
while (true)
{
auto i = block_start;
//Text range indicates the position of text where begin to output
//The output length is the min between len and the second of text range.
auto text_range = _m_locate(i, pos);
if (text_range.second > len)
text_range.second = len;
fblock * fblock_ptr = i->fblock_ptr;
data * data_ptr = i->data_ptr;
const int w = static_cast<int>(rs.allowed_width) - rs.pos.x;
nana::size sz = data_ptr->size();
if ((static_cast<int>(sz.width) > w) && (rs.pos.x != px.x_base))
{
//Change a new line
rs.pos.y += static_cast<int>(px.pixels);
px = rs.pixels[++rs.index];
rs.pos.x = px.x_base;
}
const int y = rs.pos.y + _m_text_top(px, fblock_ptr, data_ptr);
_m_change_font(graph, fblock_ptr);
if (text_range.second == data_ptr->text().length())
{
graph.string(rs.pos.x, y, _m_fgcolor(fblock_ptr), data_ptr->text());
}
else
{
nana::string str = data_ptr->text().substr(text_range.first, text_range.second);
graph.string(rs.pos.x, y, _m_fgcolor(fblock_ptr), str);
sz = graph.text_extent_size(str);
}
_m_inser_if_traceable(rs.pos.x, y, sz, fblock_ptr);
rs.pos.x += static_cast<int>(sz.width);
if(text_range.second < len)
{
len -= text_range.second;
pos += text_range.second;
}
else
break;
}
}
}
std::pair<std::size_t, std::size_t> _m_locate(dstream::linecontainer::iterator& i, std::size_t pos)
{
std::pair<std::size_t, std::size_t> r;
std::size_t n = i->data_ptr->text().length();
while(pos >= n)
{
pos -= n;
n = (++i)->data_ptr->text().length();
}
return{ pos, n - pos };
}
private:
dstream dstream_;
bool format_enabled_ = false;
nana::widgets::skeletons::fblock * fblock_ = nullptr;
std::deque<traceable> traceable_;
nana::paint::font font_;
struct def_font_tag
{
nana::string font_name;
std::size_t font_size;
bool font_bold;
nana::color_t fgcolor;
}def_;
};
//class trigger
//@brief: Draw the label
struct trigger::impl_t
{
widget * wd{nullptr};
paint::graphics * graph{nullptr};
align text_align{align::left};
align_v text_align_v;
class renderer renderer;
nana::string target; //It indicates which target is tracing.
nana::string url;
void add_listener(std::function<void(command, const nana::string&)>&& fn)
{
listener_.emplace_back(std::move(fn));
}
void call_listener(command cmd, const nana::string& tar)
{
for (auto & fn : listener_)
fn(cmd, tar);
}
private:
std::vector<std::function<void(command, const nana::string&)>> listener_;
};
trigger::trigger()
:impl_(new impl_t)
{}
trigger::~trigger()
{
delete impl_;
}
trigger::impl_t * trigger::impl() const
{
return impl_;
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
impl_->graph = &graph;
impl_->wd = &widget;
}
void trigger::mouse_move(graph_reference, const arg_mouse& arg)
{
nana::string target, url;
if(impl_->renderer.find(arg.pos.x, arg.pos.y, target, url))
{
int cur_state = 0;
if(target != impl_->target)
{
if(impl_->target.size())
{
impl_->call_listener(command::leave, impl_->target);
cur_state = 1; //Set arrow
}
impl_->target = target;
if(target.size())
{
impl_->call_listener(command::enter, impl_->target);
cur_state = 2; //Set hand
}
}
if (url != impl_->url)
{
if (impl_->url.size())
cur_state = 1; //Set arrow
impl_->url = url;
if (url.size())
cur_state = 2; //Set hand
}
if (cur_state)
impl_->wd->cursor(1 == cur_state ? cursor::arrow : cursor::hand);
}
else
{
bool restore = false;
if (impl_->target.size())
{
impl_->call_listener(command::leave, impl_->target);
impl_->target.clear();
restore = true;
}
if (impl_->url.size())
{
impl_->url.clear();
restore = true;
}
if(restore)
impl_->wd->cursor(cursor::arrow);
}
}
void trigger::mouse_leave(graph_reference, const arg_mouse&)
{
if(impl_->target.size())
{
impl_->call_listener(command::leave, impl_->target);
impl_->target.clear();
impl_->wd->cursor(cursor::arrow);
}
}
void trigger::click(graph_reference, const arg_mouse&)
{
//make a copy, because the listener may popup a window, and then
//user moves the mouse. it will reset the url when the mouse is moving out from the element.
auto url = impl_->url;
if(impl_->target.size())
impl_->call_listener(command::click, impl_->target);
system::open_url(url);
}
void trigger::refresh(graph_reference graph)
{
if(nullptr == impl_->wd) return;
window wd = impl_->wd->handle();
if(bground_mode::basic != API::effects_bground_mode(wd))
graph.rectangle(API::background(wd), true);
impl_->renderer.render(graph, impl_->wd->foreground(), impl_->text_align, impl_->text_align_v);
}
//end class label_drawer
}//end namespace label
}//end namespace drawerbase
//
//class label
label::label(){}
label::label(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
label::label(window wd, const nana::string& text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
label::label(window wd, const nana::char_t* text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
label::label(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
label& label::transparent(bool enabled)
{
if(enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
return *this;
}
bool label::transparent() const
{
return (bground_mode::basic == API::effects_bground_mode(*this));
}
label& label::format(bool f)
{
auto impl = get_drawer_trigger().impl();
if(impl->renderer.format(f))
{
window wd = *this;
impl->renderer.parse(API::dev::window_caption(wd));
API::refresh_window(wd);
}
return *this;
}
label& label::add_format_listener(std::function<void(command, const nana::string&)> f)
{
get_drawer_trigger().impl()->add_listener(std::move(f));
return *this;
}
nana::size label::measure(unsigned limited) const
{
if(empty())
return nana::size();
auto impl = get_drawer_trigger().impl();
//First Check the graph of label
//Then take a substitute for graph when the graph of label is zero-sized.
nana::paint::graphics * graph_ptr = impl->graph;
nana::paint::graphics substitute;
if(graph_ptr->empty())
{
graph_ptr = &substitute;
graph_ptr->make(10, 10);
}
return impl->renderer.measure(*graph_ptr, limited, impl->text_align, impl->text_align_v);
}
label& label::text_align(align th, align_v tv)
{
internal_scope_guard isg;
auto impl = get_drawer_trigger().impl();
bool to_update = false;
if(impl->text_align != th)
{
impl->text_align = th;
to_update = true;
}
if(impl->text_align_v != tv)
{
impl->text_align_v = tv;
to_update = true;
}
if(to_update)
API::refresh_window(*this);
return *this;
}
void label::_m_caption(nana::string&& str)
{
internal_scope_guard lock;
window wd = *this;
get_drawer_trigger().impl()->renderer.parse(str);
API::dev::window_caption(wd, std::move(str));
API::refresh_window(wd);
}
//end class label
}//end namespace nana

File diff suppressed because it is too large Load Diff

1389
source/gui/widgets/menu.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,608 @@
/*
* A Menubar implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2009-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/menubar.cpp
*/
#include <nana/gui/widgets/menubar.hpp>
#include <stdexcept>
namespace nana
{
class menu_accessor
{
public:
static void popup(menu& m, window wd, int x, int y)
{
m._m_popup(wd, x, y, true);
}
};
namespace drawerbase
{
namespace menubar
{
struct item_type
{
item_type(const nana::string& text, unsigned long shortkey)
: text(text), shortkey(shortkey)
{}
nana::string text;
unsigned long shortkey;
nana::menu menu_obj;
nana::point pos;
nana::size size;
};
class trigger::itembase
{
public:
typedef std::vector<item_type*> container;
~itembase()
{
for(auto i : cont_)
delete i;
}
void append(const nana::string& text, unsigned long shortkey)
{
if(shortkey && shortkey < 0x61) shortkey += (0x61 - 0x41);
cont_.push_back(new item_type(text, shortkey));
}
nana::menu* get_menu(std::size_t index) const
{
return (index < cont_.size() ? &(cont_[index]->menu_obj) : nullptr);
}
const item_type& at(std::size_t index) const
{
return *cont_.at(index);
}
std::size_t find(unsigned long shortkey) const
{
if(shortkey)
{
if(shortkey < 0x61) shortkey += (0x61 - 0x41);
std::size_t index = 0;
for(auto i : cont_)
{
if(i->shortkey == shortkey)
return index;
++index;
}
}
return npos;
}
const container& cont() const
{
return cont_;
}
private:
container cont_;
};
//class item_renderer
item_renderer::item_renderer(window wd, graph_reference graph)
:handle_(wd), graph_(graph)
{}
void item_renderer::background(const nana::point& pos, const nana::size& size, state_t state)
{
nana::color_t bground = API::background(handle_);
nana::color_t border, body, corner;
switch(state)
{
case item_renderer::state_highlight:
border = nana::color::highlight;
body = 0xC0DDFC;
corner = paint::graphics::mix(body, bground, 0.5);
break;
case item_renderer::state_selected:
border = nana::color::dark_border;
body = 0xFFFFFF;
corner = paint::graphics::mix(border, bground, 0.5);
break;
default: //Don't process other states.
return;
}
nana::rectangle r(pos, size);
graph_.rectangle(r, border, false);
graph_.set_pixel(pos.x, pos.y, corner);
graph_.set_pixel(pos.x + size.width - 1, pos.y, corner);
graph_.set_pixel(pos.x, pos.y + size.height - 1, corner);
graph_.set_pixel(pos.x + size.width - 1, pos.y + size.height - 1, corner);
graph_.rectangle(r.pare_off(1), body, true);
}
void item_renderer::caption(int x, int y, const nana::string& text)
{
graph_.string(x, y, 0x0, text);
}
//end class item_renderer
//class trigger
trigger::trigger()
: items_(new itembase)
{}
trigger::~trigger()
{
delete items_;
}
nana::menu* trigger::push_back(const nana::string& text)
{
nana::string::value_type shkey;
API::transform_shortkey_text(text, shkey, nullptr);
if(shkey)
API::register_shortkey(widget_->handle(), shkey);
auto i = items_->cont().size();
items_->append(text, shkey);
_m_draw();
return items_->get_menu(i);
}
nana::menu* trigger::at(std::size_t index) const
{
return items_->get_menu(index);
}
std::size_t trigger::size() const
{
return items_->cont().size();
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
graph_ = &graph;
widget_ = &widget;
}
void trigger::refresh(graph_reference)
{
_m_draw();
API::lazy_refresh();
}
void trigger::mouse_move(graph_reference, const arg_mouse& arg)
{
if (arg.pos != state_.mouse_pos)
state_.nullify_mouse = false;
bool popup = false;
if(state_.behavior == state_type::behavior_focus)
{
std::size_t index = _m_item_by_pos(arg.pos);
if(index != npos && state_.active != index)
{
state_.active = index;
popup = true;
}
}
else
popup = _m_track_mouse(arg.pos);
if(popup)
{
_m_popup_menu();
_m_draw();
API::lazy_refresh();
}
state_.mouse_pos = arg.pos;
}
void trigger::mouse_leave(graph_reference graph, const arg_mouse& arg)
{
state_.nullify_mouse = false;
mouse_move(graph, arg);
}
void trigger::mouse_down(graph_reference graph, const arg_mouse& arg)
{
state_.nullify_mouse = false;
state_.active = _m_item_by_pos(arg.pos);
if(state_.menu_active == false)
{
if(state_.active != npos)
{
state_.menu_active = true;
_m_popup_menu();
}
else
_m_total_close();
}
else if(npos == state_.active)
_m_total_close();
else
_m_popup_menu();
_m_draw();
API::lazy_refresh();
}
void trigger::mouse_up(graph_reference graph, const arg_mouse&)
{
state_.nullify_mouse = false;
if(state_.behavior != state_.behavior_menu)
{
if(state_.menu_active)
state_.behavior = state_.behavior_menu;
}
else
{
state_.behavior = state_.behavior_none;
_m_total_close();
_m_draw();
API::lazy_refresh();
}
}
void trigger::focus(graph_reference, const arg_focus& arg)
{
if((arg.getting == false) && (state_.active != npos))
{
state_.behavior = state_type::behavior_none;
state_.nullify_mouse = true;
state_.menu_active = false;
_m_close_menu();
state_.active = npos;
_m_draw();
API::lazy_refresh();
}
}
void trigger::key_press(graph_reference, const arg_keyboard& arg)
{
state_.nullify_mouse = true;
if(state_.menu)
{
switch(arg.key)
{
case keyboard::os_arrow_down:
state_.menu->goto_next(true); break;
case keyboard::backspace:
case keyboard::os_arrow_up:
state_.menu->goto_next(false); break;
case keyboard::os_arrow_right:
if(state_.menu->goto_submen() == false)
_m_move(false);
break;
case keyboard::os_arrow_left:
if(state_.menu->exit_submenu() == false)
_m_move(true);
break;
case keyboard::escape:
if(state_.menu->exit_submenu() == false)
{
_m_close_menu();
state_.behavior = state_.behavior_focus;
state_.menu_active = false;
}
break;
case keyboard::enter:
state_.menu->pick();
break;
default:
if(2 != state_.menu->send_shortkey(arg.key))
{
if(state_.active != npos)
{
_m_total_close();
if(arg.key == 18) //ALT
state_.behavior = state_.behavior_focus;
}
}
else
state_.menu->goto_submen();
}
}
else
{
switch(arg.key)
{
case keyboard::os_arrow_right:
_m_move(false);
break;
case keyboard::backspace:
case keyboard::os_arrow_left:
_m_move(true);
break;
case keyboard::escape:
if(state_.behavior == state_.behavior_focus)
{
state_.active= npos;
state_.behavior = state_.behavior_none;
API::restore_menubar_taken_window();
}
}
}
_m_draw();
API::lazy_refresh();
}
void trigger::key_release(graph_reference, const arg_keyboard& arg)
{
if(arg.key == 18)
{
if(state_.behavior == state_type::behavior_none)
{
state_.behavior = state_type::behavior_focus;
state_.active = 0;
}
else
{
state_.behavior = state_type::behavior_none;
nana::point pos = API::cursor_position();
API::calc_window_point(widget_->handle(), pos);
state_.active = _m_item_by_pos(pos);
}
state_.menu_active = false;
_m_draw();
API::lazy_refresh();
}
}
void trigger::shortkey(graph_reference graph, const arg_keyboard& arg)
{
API::focus_window(widget_->handle());
std::size_t index = items_->find(arg.key);
if(index != npos && (index != state_.active || nullptr == state_.menu))
{
_m_close_menu();
state_.menu_active = true;
state_.nullify_mouse = true;
state_.active = index;
if(_m_popup_menu())
state_.menu->goto_next(true);
_m_draw();
API::lazy_refresh();
state_.behavior = state_.behavior_menu;
}
}
void trigger::_m_move(bool to_left)
{
if(items_->cont().empty()) return;
const std::size_t last_pos = items_->cont().size() - 1;
std::size_t index = state_.active;
if(to_left)
{
--index;
if (index > last_pos)
index = last_pos;
}
else
{
++index;
if(index > last_pos)
index = 0;
}
if(index != state_.active)
{
state_.active = index;
_m_draw();
API::lazy_refresh();
if(_m_popup_menu())
state_.menu->goto_next(true);
}
}
bool trigger::_m_popup_menu()
{
if(state_.menu_active && (state_.menu != items_->get_menu(state_.active)))
{
std::size_t index = state_.active;
_m_close_menu();
state_.active = index;
state_.menu = items_->get_menu(state_.active);
if(state_.menu)
{
const item_type &m = items_->at(state_.active);
state_.menu->destroy_answer(std::bind(&trigger::_m_unload_menu_window, this));
menu_accessor::popup(*state_.menu, widget_->handle(), m.pos.x, m.pos.y + m.size.height);
return true;
}
}
return false;
}
void trigger::_m_total_close()
{
_m_close_menu();
state_.menu_active = false;
state_.behavior = state_.behavior_none;
API::restore_menubar_taken_window();
auto pos = API::cursor_position();
API::calc_window_point(widget_->handle(), pos);
state_.active = _m_item_by_pos(pos);
}
bool trigger::_m_close_menu()
{
if(state_.menu)
{
state_.passive_close = false;
state_.menu->close();
state_.passive_close = true;
state_.menu = nullptr;
return true;
}
return false;
}
void trigger::_m_unload_menu_window()
{
state_.menu = nullptr;
if(state_.passive_close)
{
_m_total_close();
_m_draw();
API::update_window(widget_->handle());
}
}
std::size_t trigger::_m_item_by_pos(const ::nana::point& pos)
{
if((2 <= pos.x) && (2 <= pos.y) && (pos.y < 25))
{
int item_x = 2;
std::size_t index = 0;
for(auto i : items_->cont())
{
if(item_x <= pos.x && pos.x < item_x + static_cast<int>(i->size.width))
return index;
item_x += i->size.width;
++index;
}
}
return npos;
}
bool trigger::_m_track_mouse(const ::nana::point& pos)
{
if(state_.nullify_mouse == false)
{
std::size_t which = _m_item_by_pos(pos);
if((which != state_.active) && (which != npos || (false == state_.menu_active)))
{
state_.active = which;
return true;
}
}
return false;
}
void trigger::_m_draw()
{
nana::color_t bground_color = API::background(*widget_);
graph_->rectangle(bground_color, true);
item_renderer ird(*widget_, *graph_);
nana::point item_pos(2, 2);
nana::size item_s(0, 23);
unsigned long index = 0;
for(auto i : items_->cont())
{
//Transform the text if it contains the hotkey character
nana::string::value_type hotkey;
nana::string::size_type hotkey_pos;
nana::string text = API::transform_shortkey_text(i->text, hotkey, &hotkey_pos);
nana::size text_s = graph_->text_extent_size(text);
item_s.width = text_s.width + 16;
i->pos = item_pos;
i->size = item_s;
item_renderer::state_t state = (index != state_.active ? ird.state_normal : (state_.menu_active ? ird.state_selected : ird.state_highlight));
ird.background(item_pos, item_s, state);
if(state == ird.state_selected)
{
int x = item_pos.x + item_s.width;
int y1 = item_pos.y + 2, y2 = item_pos.y + item_s.height - 1;
graph_->line(x, y1, x, y2, paint::graphics::mix(color::gray_border, bground_color, 0.6));
graph_->line(x + 1, y1, x + 1, y2, paint::graphics::mix(color::button_face_shadow_end, bground_color, 0.5));
}
//Draw text, the text is transformed from orignal for hotkey character
int text_top_off = (item_s.height - text_s.height) / 2;
ird.caption(item_pos.x + 8, item_pos.y + text_top_off, text);
if(hotkey)
{
unsigned off_w = (hotkey_pos ? graph_->text_extent_size(text, static_cast<unsigned>(hotkey_pos)).width : 0);
nana::size hotkey_size = graph_->text_extent_size(text.c_str() + hotkey_pos, 1);
unsigned ascent, descent, inleading;
graph_->text_metrics(ascent, descent, inleading);
int x = item_pos.x + 8 + off_w;
int y = item_pos.y + text_top_off + ascent + 1;
graph_->line(x, y, x + hotkey_size.width - 1, y, 0x0);
}
item_pos.x += i->size.width;
++index;
}
}
//struct state_type
trigger::state_type::state_type()
:active(npos), behavior(behavior_none), menu_active(false), passive_close(true), nullify_mouse(false), menu(nullptr)
{}
//end struct state_type
//end class trigger
}//end namespace menubar
}//end namespace drawerbase
//class menubar
menubar::menubar(){}
menubar::menubar(window wd)
{
create(wd);
}
void menubar::create(window wd)
{
widget_object<category::widget_tag, drawerbase::menubar::trigger>
::create(wd, rectangle(nana::size(API::window_size(wd).width, 28)));
API::attach_menubar(handle());
}
menu& menubar::push_back(const nana::string& text)
{
return *(get_drawer_trigger().push_back(text));
}
menu& menubar::at(std::size_t index) const
{
menu* p = get_drawer_trigger().at(index);
if(nullptr == p)
throw std::out_of_range("menubar::at, out of range");
return *p;
}
std::size_t menubar::length() const
{
return get_drawer_trigger().size();
}
//end class menubar
}//end namespace nana

View File

@@ -0,0 +1,42 @@
/*
* A Panel Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: source/gui/widgets/panel.cpp
*
* @brief: panel is a widget used for placing some widgets.
*/
#include <nana/gui/widgets/panel.hpp>
namespace nana
{
namespace drawerbase
{
namespace panel
{
//class drawer
drawer::drawer()
:window_(nullptr)
{}
void drawer::attached(widget_reference widget, graph_reference)
{
widget.caption(STR("Nana Panel"));
window_ = widget.handle();
}
void drawer::refresh(graph_reference graph)
{
if(bground_mode::basic != API::effects_bground_mode(window_))
graph.rectangle(API::background(window_), true);
}
//end class drawer
}//end namespace panel
}//end namespace drawerbase
}//end namespace nana

View File

@@ -0,0 +1,299 @@
/*
* A Picture Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/picture.cpp
* @description:
* Used for showing a picture
*/
#include <nana/gui/widgets/picture.hpp>
#include <nana/paint/image.hpp>
namespace nana
{
namespace xpicture
{
//class picture_drawer
picture_drawer::picture_drawer():graph_(nullptr)
{
backimg_.arg = nana::arrange::unknown;
backimg_.beg = backimg_.end = 0;
}
picture_drawer::runtime_type::runtime_type()
:background_shadow_start(0), background_shadow_end(0), horizontal(true)
{}
void picture_drawer::attached(widget_reference& widget, graph_reference graph)
{
widget_ = &widget;
graph_ = &graph;
}
void picture_drawer::load(const nana::char_t* file)
{
backimg_.image.open(file);
}
void picture_drawer::load(const nana::paint::image& img)
{
backimg_.image = img;
}
void picture_drawer::set_shadow_background(unsigned begin_color, unsigned end_color, bool horizontal)
{
runtime_.background_shadow_end = end_color;
runtime_.background_shadow_start = begin_color;
runtime_.horizontal = horizontal;
_m_draw_background();
}
bool picture_drawer::bgstyle(bool is_stretch, nana::arrange arg, int beg, int end)
{
if(backimg_.image)
{
backimg_.is_stretch = is_stretch;
backimg_.arg = arg;
if(arg == nana::arrange::horizontal_vertical) return false;
if(beg < 0) beg = 0;
if(end < beg) end = beg;
if((backimg_.beg == beg) && (backimg_.end == end)) return false;
nana::size imgsize = backimg_.image.size();
unsigned scale = (arg == nana::arrange::horizontal ? imgsize.width : imgsize.height);
if(beg < 0)
beg = 0;
else if(static_cast<unsigned>(beg) >= scale)
beg = static_cast<int>(scale) - 1;
if(end < beg)
end = beg;
else if(static_cast<unsigned>(end) >= scale)
end = static_cast<int>(scale) - 1;
backimg_.beg = beg;
backimg_.end = end;
return true;
}
backimg_.arg = nana::arrange::unknown;
return false;
}
void picture_drawer::refresh(graph_reference graph)
{
if(graph.changed())
{
_m_draw_background();
if(backimg_.image.empty() == false)
{
nana::size imgsize = backimg_.image.size();
nana::size gsize = graph.size();
switch(backimg_.arg)
{
case nana::arrange::unknown:
backimg_.image.paste(graph, 0, 0);
break;
case nana::arrange::horizontal:
if(backimg_.beg < backimg_.end)
{
unsigned block_tail = imgsize.width - backimg_.end;
if(backimg_.beg)
backimg_.image.paste(nana::rectangle(0, 0, backimg_.beg, imgsize.height), graph, nana::point());
if(block_tail)
backimg_.image.paste(nana::rectangle(static_cast<int>(imgsize.width - block_tail), 0, block_tail, imgsize.height), graph, nana::point(gsize.width - block_tail, 0));
if(backimg_.beg < backimg_.end)
{
unsigned fixed_size = backimg_.beg + block_tail;
if(fixed_size < gsize.width)
{
if(false == backimg_.is_stretch)
{
unsigned imgarea = backimg_.end - backimg_.beg;
fixed_size = gsize.width - fixed_size;
nana::rectangle r(backimg_.beg, 0, imgarea, imgsize.height);
nana::point p_dst(backimg_.beg, 0);
while(imgarea < fixed_size)
{
backimg_.image.paste(r, graph, p_dst);
p_dst.x += static_cast<int>(imgarea);
fixed_size -= imgarea;
}
if(fixed_size)
{
r.width = fixed_size;
backimg_.image.paste(r, graph, p_dst);
}
}
else
backimg_.image.stretch(nana::rectangle(backimg_.beg, 0, imgsize.width - fixed_size, imgsize.height), graph, nana::rectangle(backimg_.beg, 0, gsize.width - fixed_size, imgsize.height));
}
}
}
else
{
if(false == backimg_.is_stretch)
{
int x = 0;
while(x < static_cast<int>(gsize.width))
{
backimg_.image.paste(graph, x, 0);
x += static_cast<int>(imgsize.width);
}
}
else
backimg_.image.stretch(imgsize, graph, nana::size(gsize.width, imgsize.height));
}
break;
case nana::arrange::vertical:
if(backimg_.beg < backimg_.end)
{
unsigned block_tail = imgsize.height - backimg_.end;
if(backimg_.beg)
backimg_.image.paste(nana::rectangle(0, 0, imgsize.width, static_cast<unsigned>(backimg_.beg)), graph, nana::point());
if(block_tail)
backimg_.image.paste(nana::rectangle(0, static_cast<int>(imgsize.height - block_tail), imgsize.width, block_tail), graph, nana::point(0, gsize.height - block_tail));
if(backimg_.beg < backimg_.end)
{
unsigned fixed_size = backimg_.beg + block_tail;
if(fixed_size < gsize.height)
{
if(false == backimg_.is_stretch)
{
unsigned imgarea = backimg_.end - backimg_.beg;
fixed_size = gsize.height - fixed_size;
nana::rectangle r(0, backimg_.beg, imgsize.width, imgarea);
nana::point pos(0, backimg_.beg);
while(imgarea < fixed_size)
{
backimg_.image.paste(r, graph, pos);
pos.y += static_cast<int>(imgarea);
fixed_size -= imgarea;
}
if(fixed_size)
{
r.height = fixed_size;
backimg_.image.paste(r, graph, pos);
}
}
else
backimg_.image.stretch(nana::rectangle(0, backimg_.beg, imgsize.width, imgsize.height - fixed_size), graph, nana::rectangle(0, backimg_.beg, imgsize.width, gsize.height - fixed_size));
}
}
}
else
{
if(false == backimg_.is_stretch)
{
int y = 0;
while(y < static_cast<int>(gsize.height))
{
backimg_.image.paste(graph, 0, y);
y += static_cast<int>(imgsize.height);
}
}
else
backimg_.image.stretch(imgsize, graph, nana::rectangle(0, 0, imgsize.width, gsize.height));
}
break;
case nana::arrange::horizontal_vertical:
if(backimg_.is_stretch == false)
{
int y = 0;
while(y < static_cast<int>(gsize.height))
{
int x = 0;
while(x < static_cast<int>(gsize.width))
{
backimg_.image.paste(graph, x, y);
x += static_cast<int>(imgsize.width);
}
y += static_cast<int>(imgsize.height);
}
}
else
backimg_.image.stretch(imgsize, graph, gsize);
break;
}
}
}
}
void picture_drawer::_m_draw_background()
{
if(graph_ && (bground_mode::basic != API::effects_bground_mode(*widget_)))
{
if(runtime_.background_shadow_end == runtime_.background_shadow_start)
graph_->rectangle((runtime_.background_shadow_end ? runtime_.background_shadow_end : widget_->background()), true);
else
graph_->shadow_rectangle(graph_->size(), runtime_.background_shadow_start, runtime_.background_shadow_end, !runtime_.horizontal);
}
}
//end class picture_drawer
}//end namespace xpicture
//class picture
picture::picture(){}
picture::picture(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
picture::picture(window wd, const nana::rectangle& r, bool visible)
{
create(wd, r, visible);
}
void picture::load(const nana::paint::image& img)
{
get_drawer_trigger().load(img);
API::refresh_window(*this);
}
void picture::bgstyle(bool stretchable, nana::arrange arg, int beg, int end)
{
if(get_drawer_trigger().bgstyle(stretchable, arg, beg, end))
API::refresh_window(*this);
}
void picture::set_shadow_background(unsigned begin_color, unsigned end_color, bool horizontal)
{
get_drawer_trigger().set_shadow_background(begin_color, end_color, horizontal);
}
void picture::transparent(bool enabled)
{
if(enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
}
bool picture::transparent() const
{
return (bground_mode::basic == API::effects_bground_mode(*this));
}
//end class picture
}//end namespace nana

View File

@@ -0,0 +1,205 @@
/*
* A Progress Indicator Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/progress.cpp
*/
#include <nana/gui/widgets/progress.hpp>
namespace nana
{
namespace drawerbase
{
namespace progress
{
//class trigger
trigger::trigger()
: graph_(nullptr), draw_width_(static_cast<unsigned>(-1)), has_value_(true),
unknown_(false), max_(100), value_(0)
{}
void trigger::attached(widget_reference wd, graph_reference graph)
{
widget_ = &wd;
graph_ = &graph;
}
unsigned trigger::value() const
{
return value_;
}
unsigned trigger::value(unsigned v)
{
internal_scope_guard isg;
if(false == unknown_)
{
if(value_ != v)
value_ = v > max_?max_:v;
}
else
value_ += (v?10:v);
if(_m_check_changing(value_))
{
_m_draw();
API::update_window(widget_->handle());
}
return v;
}
unsigned trigger::inc()
{
internal_scope_guard isg;
if(false == unknown_)
{
if(value_ < max_)
++value_;
}
else
value_ += 5;
if(_m_check_changing(value_))
API::refresh_window(widget_->handle());
return value_;
}
unsigned trigger::Max() const
{
return max_;
}
unsigned trigger::Max(unsigned value)
{
max_ = value;
if(max_ == 0) ++max_;
API::refresh_window(widget_->handle());
return max_;
}
void trigger::unknown(bool enb)
{
unknown_ = enb;
if(enb)
draw_width_ = static_cast<unsigned>(-1);
}
bool trigger::unknown() const
{
return unknown_;
}
void trigger::refresh(graph_reference)
{
_m_draw();
}
void trigger::_m_draw()
{
if(false == unknown_)
draw_width_ = static_cast<unsigned>((graph_->width() - border * 2) * (double(value_) / max_));
_m_draw_box(*graph_);
_m_draw_progress(*graph_);
}
void trigger::_m_draw_box(graph_reference graph)
{
rectangle r = graph.size();
graph.shadow_rectangle(r, color::button_face_shadow_end, color::button_face_shadow_start, true);
graph.rectangle_line(r, 0x808080, 0x808080, 0xFFFFFF, 0xFFFFFF);
}
void trigger::_m_draw_progress(graph_reference graph)
{
unsigned width = graph.width() - border * 2;
unsigned height = graph.height() - border * 2;
if(false == unknown_)
{
if(draw_width_)
graph.shadow_rectangle(border, border, draw_width_, height, 0x6FFFA8, 0x107515, true);
}
else
{
unsigned block = width / 3;
int left = (value_ < block ? 0 : value_ - block) + border;
int right = (value_ >= width - 1 + border? width - 1 + border: value_);
if(right >= left)
graph.shadow_rectangle(left, border, right - left + 1, height, 0x6FFFA8, 0x107515, true);
if(value_ >= width + block) value_ = 0;
}
}
bool trigger::_m_check_changing(unsigned newvalue) const
{
if(graph_)
return (((graph_->width() - border * 2) * newvalue / max_) != draw_width_);
return false;
}
//end class drawer
}//end namespace progress
}//end namespace drawerbase
//class progress
progress::progress(){}
progress::progress(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
progress::progress(window wd, const rectangle & r, bool visible)
{
create(wd, r, visible);
}
unsigned progress::value() const
{
return get_drawer_trigger().value();
}
unsigned progress::value(unsigned val)
{
internal_scope_guard isg;
if(API::empty_window(this->handle()) == false)
return get_drawer_trigger().value(val);
return 0;
}
unsigned progress::inc()
{
internal_scope_guard isg;
return get_drawer_trigger().inc();
}
unsigned progress::amount() const
{
return get_drawer_trigger().Max();
}
unsigned progress::amount(unsigned value)
{
return get_drawer_trigger().Max(value);
}
void progress::unknown(bool enb)
{
get_drawer_trigger().unknown(enb);
}
bool progress::unknown() const
{
return get_drawer_trigger().unknown();
}
//end class progress
}//end namespace nana

View File

@@ -0,0 +1,341 @@
/*
* A Scroll Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/scroll.cpp
*/
#include <nana/gui/widgets/scroll.hpp>
namespace nana
{
namespace drawerbase
{
namespace scroll
{
//struct metrics_type
metrics_type::metrics_type()
:peak(1), range(1), step(1), value(0),
what(buttons::none), pressed(false), scroll_length(0), scroll_pos(0)
{}
//end struct metrics_type
//class drawer
drawer::drawer(metrics_type& m)
:metrics_(m)
{}
void drawer::set_vertical(bool v)
{
vertical_ = v;
}
buttons drawer::what(graph_reference graph, const point& screen_pos)
{
unsigned scale;
int pos;
if(vertical_)
{
scale = graph.height();
pos = screen_pos.y;
}
else
{
scale = graph.width();
pos = screen_pos.x;
}
if(scale >= fixedsize * 2)
{
if(pos < static_cast<int>(fixedsize))
return buttons::first;
if(pos > static_cast<int>(scale - fixedsize))
return buttons::second;
}
else
{
if(pos < static_cast<int>(scale / 2))
return buttons::first;
if(pos > static_cast<int>(scale / 2))
return buttons::second;
}
if(metrics_.scroll_length)
{
if(metrics_.scroll_pos + static_cast<int>(fixedsize) <= pos && pos < metrics_.scroll_pos + static_cast<int>(fixedsize + metrics_.scroll_length))
return buttons::scroll;
}
if(static_cast<int>(fixedsize) <= pos && pos < metrics_.scroll_pos)
return buttons::forward;
else if(metrics_.scroll_pos + static_cast<int>(metrics_.scroll_length) <= pos && pos < static_cast<int>(scale - fixedsize))
return buttons::backward;
return buttons::none;
}
void drawer::scroll_delta_pos(graph_reference graph, int mouse_pos)
{
if(mouse_pos + metrics_.scroll_mouse_offset == metrics_.scroll_pos) return;
unsigned scale = vertical_ ? graph.height() : graph.width();
if(scale > fixedsize * 2)
{
int pos = mouse_pos - metrics_.scroll_mouse_offset;
const unsigned scroll_area = static_cast<unsigned>(scale - fixedsize * 2 - metrics_.scroll_length);
if(pos < 0)
pos = 0;
else if(pos > static_cast<int>(scroll_area))
pos = static_cast<int>(scroll_area);
metrics_.scroll_pos = pos;
auto value_max = metrics_.peak - metrics_.range;
metrics_.value = pos * value_max / scroll_area;
if(metrics_.value < metrics_.peak - metrics_.range)
{
int selfpos = static_cast<int>(metrics_.value * scroll_area / value_max);
int nextpos = static_cast<int>((metrics_.value + 1) * scroll_area / value_max);
if(selfpos != nextpos && (pos - selfpos > nextpos - pos))
++metrics_.value;
}
else
metrics_.value = value_max;
}
}
void drawer::auto_scroll()
{
if(_m_check())
{
if(buttons::forward == metrics_.what)
{ //backward
if(metrics_.value <= metrics_.range)
metrics_.value = 0;
else
metrics_.value -= metrics_.range;
}
else if(buttons::backward == metrics_.what)
{
if(metrics_.peak - metrics_.range - metrics_.value <= metrics_.range)
metrics_.value = metrics_.peak - metrics_.range;
else
metrics_.value += metrics_.range;
}
}
}
void drawer::draw(graph_reference graph, buttons what)
{
if(false == metrics_.pressed || metrics_.what != buttons::scroll)
_m_adjust_scroll(graph);
_m_background(graph);
unsigned width, height;
int x, y;
if(vertical_)
{
x = 0;
y = graph.height() - fixedsize;
width = graph.width();
height = fixedsize;
}
else
{
x = graph.width() - fixedsize;
y = 0;
width = fixedsize;
height = graph.height();
}
int state = ((_m_check() == false || what == buttons::none) ? states::none : states::highlight);
int moused_state = (_m_check() ? (metrics_.pressed ? states::selected : states::actived) : states::none);
//draw first
_m_draw_button(graph, 0, 0, width, height, buttons::first, (buttons::first == what ? moused_state : state));
//draw second
_m_draw_button(graph, x, y, width, height, buttons::second, (buttons::second == what ? moused_state : state));
//draw scroll
_m_draw_scroll(graph, (buttons::scroll == what ? moused_state : states::highlight));
}
//private:
void drawer::_m_background(graph_reference graph)
{
graph.rectangle(0xF0F0F0, true);
if(metrics_.pressed && _m_check())
{
int x = 0, y = 0;
unsigned width = graph.width(), height = graph.height();
if(metrics_.what == buttons::forward)
{
*(vertical_ ? &y : &x) = fixedsize;
*(vertical_ ? &height: &width) = metrics_.scroll_pos;
}
else if(buttons::backward == metrics_.what)
{
*(vertical_ ? &y : &x) = static_cast<int>(fixedsize + metrics_.scroll_pos + metrics_.scroll_length);
*(vertical_ ? &height: &width) = static_cast<unsigned>((vertical_ ? graph.height() : graph.width()) - (fixedsize * 2 + metrics_.scroll_pos + metrics_.scroll_length));
}
else
return;
if(width && height)
graph.rectangle(x, y, width, height, 0xDCDCDC, true);
}
}
void drawer::_m_button_frame(graph_reference graph, int x, int y, unsigned width, unsigned height, int state)
{
if(state)
{
unsigned color = 0x979797; //highlight
switch(state)
{
case states::actived:
color = 0x86D5FD; break;
case states::selected:
color = 0x3C7FB1; break;
}
graph.rectangle(rectangle(x, y, width, height), color, false);
unsigned color_x = graph.mix(color, 0xFFFFFF, 0.5);
x += 2;
y += 2;
width -= 4;
height -= 4;
if(vertical_)
{
unsigned half = width / 2;
graph.rectangle(x + (width - half), y, half, height, color_x, true);
width -= half;
}
else
{
unsigned half = height / 2;
graph.rectangle(x, y + height - half, width, half, color_x, true);
height -= half;
}
graph.shadow_rectangle(x, y, width, height, 0xFFFFFF, color_x, !vertical_);
}
}
bool drawer::_m_check() const
{
return (metrics_.scroll_length && metrics_.range && (metrics_.peak > metrics_.range));
}
void drawer::_m_adjust_scroll(graph_reference graph)
{
if(metrics_.range == 0 || metrics_.peak <= metrics_.range) return;
unsigned pixels = vertical_ ? graph.height() : graph.width();
int pos = 0;
unsigned len = 0;
if(pixels > fixedsize * 2)
{
pixels -= (fixedsize * 2);
len = static_cast<unsigned>(pixels * metrics_.range / metrics_.peak);
if(len < fixedsize)
len = fixedsize;
if(metrics_.value)
{
pos = static_cast<int>(pixels - len);
if(metrics_.value + metrics_.range >= metrics_.peak)
metrics_.value = metrics_.peak - metrics_.range;
else
pos = static_cast<int>((metrics_.value * pos) /(metrics_.peak - metrics_.range));
}
}
metrics_.scroll_pos = pos;
metrics_.scroll_length = len;
}
void drawer::_m_draw_scroll(graph_reference graph, int state)
{
if(_m_check())
{
int x, y;
unsigned width, height;
if(vertical_)
{
x = 0;
y = fixedsize + metrics_.scroll_pos;
width = graph.width();
height = static_cast<unsigned>(metrics_.scroll_length);
}
else
{
x = fixedsize + metrics_.scroll_pos;
y = 0;
width = static_cast<unsigned>(metrics_.scroll_length);
height = graph.height();
}
_m_button_frame(graph, x, y, width, height, state);
}
}
void drawer::_m_draw_button(graph_reference graph, int x, int y, unsigned width, unsigned height, buttons what, int state)
{
if(_m_check())
_m_button_frame(graph, x, y, width, height, state);
using namespace nana::paint::gadget;
if(buttons::first == what || buttons::second == what)
{
nana::size sz = graph.size();
directions::t dir;
if(buttons::second == what)
{
if(vertical_)
{
y = static_cast<int>(sz.height - fixedsize);
dir = directions::to_south;
}
else
{
x = static_cast<int>(sz.width - fixedsize);
dir = directions::to_east;
}
}
else
dir = vertical_ ? directions::to_north : directions::to_west;
if(vertical_)
x = (static_cast<int>(sz.width) - 16) / 2;
else
y = (static_cast<int>(sz.height) - 16) / 2;
arrow_16_pixels(graph, x, y, _m_check() ? 0x0 : 0x808080, (states::none == state ? 0 : 1), dir);
}
}
//end class drawer
}//end namespace scroll
}//end namespace drawerbase
}//end namespace nana

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,788 @@
#include <nana/gui/widgets/slider.hpp>
namespace nana
{
namespace drawerbase
{
namespace slider
{
provider::~provider(){}
renderer::~renderer(){}
class interior_renderer
: public renderer
{
private:
virtual void background(window wd, graph_reference graph, bool isglass)
{
if(isglass == false)
graph.rectangle(API::background(wd), true);
}
virtual void bar(window, graph_reference graph, const bar_t& bi)
{
//draw border
const nana::color_t dark = 0x83909F;
const nana::color_t gray = 0x9DAEC2;
graph.rectangle_line(bi.r,
dark, dark, gray, gray);
}
virtual void adorn(window, graph_reference graph, const adorn_t& ad)
{
int len = ad.bound.y - ad.bound.x;
const unsigned upperblock = ad.block - ad.block / 2;
if(ad.horizontal)
{
graph.shadow_rectangle(ad.bound.x, ad.fixedpos, len, upperblock, 0x84C5FF, 0x0F41CD, true);
graph.shadow_rectangle(ad.bound.x, ad.fixedpos + upperblock, len, ad.block - upperblock, 0x0F41CD, 0x6E96FF, true);
}
else
{
graph.shadow_rectangle(ad.fixedpos, ad.bound.x, upperblock, len, 0x84C5FF, 0x0F41CD, false);
graph.shadow_rectangle(ad.fixedpos + upperblock, ad.bound.x, ad.block - upperblock, len, 0x0F41CD, 0x6E96FF, false);
}
}
virtual void adorn_textbox(window, graph_reference graph, const nana::string& str, const nana::rectangle & r)
{
graph.rectangle(r, 0xFFFFFF, false);
graph.string(r.x + 2, r.y + 1, 0xFFFFFF, str);
}
virtual void slider(window, graph_reference graph, const slider_t& s)
{
nana::rectangle r = graph.size();
if(s.horizontal)
{
r.x = s.pos;
r.width = s.scale;
}
else
{
r.y = s.pos;
r.height = s.scale;
}
graph.round_rectangle(r, 3, 3, 0x0, true, 0xF0F0F0);
}
};
class controller
{
public:
enum dir_t{DirHorizontal, DirVertical};
enum where_t{WhereNone, WhereBar, WhereSlider};
typedef drawer_trigger::graph_reference graph_reference;
controller()
{
other_.wd = nullptr;
other_.widget = nullptr;
other_.graph = nullptr;
proto_.renderer = pat::cloneable<renderer>(interior_renderer());
attr_.skdir = seekdir::bilateral;
attr_.dir = this->DirHorizontal;
attr_.vcur = 0;
attr_.vmax = 10;
attr_.slider_scale = 8;
attr_.border = 1;
attr_.is_draw_adorn = false;
}
void seek(seekdir sd)
{
attr_.skdir = sd;
}
window handle() const
{
return other_.wd;
}
void attached(nana::slider& wd, graph_reference graph)
{
other_.wd = wd.handle();
other_.widget = &wd;
other_.graph = &graph;
_m_mk_slider_pos_by_value();
}
void detached()
{
other_.graph = nullptr;
}
pat::cloneable<renderer>& ext_renderer()
{
return proto_.renderer;
}
void ext_renderer(const pat::cloneable<renderer>& rd)
{
proto_.renderer = rd;
}
void ext_provider(const pat::cloneable<provider>& pd)
{
proto_.provider = pd;
}
void draw()
{
if(other_.graph && !other_.graph->size().empty())
{
bool is_transparent = (bground_mode::basic == API::effects_bground_mode(other_.wd));
proto_.renderer->background(other_.wd, *other_.graph, is_transparent);
_m_draw_objects();
}
}
void vertical(bool v)
{
dir_t dir = (v ? this->DirVertical : this->DirHorizontal);
if(dir != attr_.dir)
{
attr_.dir = dir;
this->draw();
}
}
bool vertical() const
{
return (this->DirVertical == attr_.dir);
}
void vmax(unsigned m)
{
if(m == 0) m = 1;
if(attr_.vmax != m)
{
attr_.vmax = m;
if(attr_.vcur > m)
{
attr_.vcur = m;
_m_emit_value_changed();
}
_m_mk_slider_pos_by_value();
draw();
}
}
unsigned vmax() const
{
return attr_.vmax;
}
void vcur(unsigned v)
{
if(attr_.vmax < v)
v = attr_.vmax;
if(attr_.vcur != v)
{
attr_.vcur = v;
this->_m_mk_slider_pos_by_value();
draw();
}
}
unsigned vcur() const
{
return attr_.vcur;
}
void resize()
{
this->_m_mk_slider_pos_by_value();
attr_.adorn_pos = attr_.pos;
}
where_t seek_where(int x, int y) const
{
nana::rectangle r = _m_bar_area();
if(attr_.dir == this->DirVertical)
{
std::swap(x, y);
std::swap(r.width, r.height);
}
int pos = _m_slider_pos();
if(pos <= x && x < pos + static_cast<int>(attr_.slider_scale))
return WhereSlider;
pos = static_cast<int>(attr_.slider_scale) / 2;
if(pos <= x && x < pos + static_cast<int>(r.width))
{
if(y < r.y + static_cast<int>(r.height))
return WhereBar;
}
return WhereNone;
}
//set_slider_pos
//move the slider to a position where a mouse click on WhereBar.
bool set_slider_pos(int x, int y)
{
if(this->DirVertical == attr_.dir)
std::swap(x, y);
x -= _m_slider_refpos();
if(x < 0)
return false;
if(x > static_cast<int>(_m_scale()))
x = static_cast<int>(_m_scale());
double pos = attr_.pos;
double dx = _m_evaluate_by_seekdir(x);
attr_.pos = dx;
attr_.adorn_pos = dx;
_m_mk_slider_value_by_pos();
return (attr_.pos != pos);
}
void set_slider_refpos(::nana::point pos)
{
if(this->DirVertical == attr_.dir)
std::swap(pos.x, pos.y);
slider_state_.trace = slider_state_.TraceCapture;
slider_state_.snap_pos = static_cast<int>(attr_.pos);
slider_state_.refpos = pos;
API::capture_window(other_.wd, true);
}
bool release_slider()
{
if(slider_state_.trace == slider_state_.TraceCapture)
{
API::capture_window(other_.wd, false);
if(other_.wd != API::find_window(API::cursor_position()))
{
slider_state_.trace = slider_state_.TraceNone;
attr_.is_draw_adorn = false;
}
else
slider_state_.trace = slider_state_.TraceOver;
_m_mk_slider_value_by_pos();
_m_mk_slider_pos_by_value();
return true;
}
return false;
}
bool if_trace_slider() const
{
return (slider_state_.trace == slider_state_.TraceCapture);
}
bool move_slider(int x, int y)
{
int mpos = (this->DirHorizontal == attr_.dir ? x : y);
int pos = slider_state_.snap_pos + (mpos - slider_state_.refpos.x);
if(pos > 0)
{
int scale = static_cast<int>(_m_scale());
if(pos > scale)
pos = scale;
}
else
pos = 0;
double dstpos = _m_evaluate_by_seekdir(pos);
attr_.is_draw_adorn = true;
if(dstpos != attr_.pos)
{
attr_.pos = dstpos;
attr_.adorn_pos = dstpos;
return true;
}
return false;
}
bool move_adorn(int x, int y)
{
double xpos = (this->DirHorizontal == attr_.dir ? x : y);
xpos -= _m_slider_refpos();
if(xpos > static_cast<int>(_m_scale()))
xpos = static_cast<int>(_m_scale());
int pos = static_cast<int>(attr_.adorn_pos);
xpos = _m_evaluate_by_seekdir(xpos);
attr_.adorn_pos = xpos;
attr_.is_draw_adorn = true;
if(slider_state_.trace == slider_state_.TraceNone)
slider_state_.trace = slider_state_.TraceOver;
return (pos != static_cast<int>(xpos));
}
unsigned move_step(bool forward)
{
unsigned cmpvalue = attr_.vcur;
if(forward)
{
if(attr_.vcur)
--attr_.vcur;
}
else if(attr_.vcur < attr_.vmax)
++attr_.vcur;
if(cmpvalue != attr_.vcur)
{
_m_mk_slider_pos_by_value();
draw();
_m_emit_value_changed();
}
return cmpvalue;
}
unsigned adorn() const
{
return _m_value_by_pos(attr_.adorn_pos);
}
bool reset_adorn()
{
//Test if the slider is captured, the operation should be ignored. Because the mouse_leave always be generated even through
//the slider is captured.
if(slider_state_.trace == slider_state_.TraceCapture && (nana::API::capture_window() == this->other_.wd))
return false;
slider_state_.trace = slider_state_.TraceNone;
attr_.is_draw_adorn = false;
if(attr_.adorn_pos != attr_.pos)
{
attr_.adorn_pos = attr_.pos;
return true;
}
return false;
}
private:
void _m_emit_value_changed() const
{
other_.widget->events().value_changed.emit(::nana::arg_slider{ *other_.widget });
}
nana::rectangle _m_bar_area() const
{
auto sz = other_.graph->size();
nana::rectangle r = sz;
if(this->DirHorizontal == attr_.dir)
{
r.x = attr_.slider_scale / 2 - attr_.border;
r.width = (static_cast<int>(sz.width) > (r.x << 1) ? sz.width - (r.x << 1) : 0);
}
else
{
r.y = attr_.slider_scale / 2 - attr_.border;
r.height = (static_cast<int>(sz.height) > (r.y << 1) ? sz.height - (r.y << 1) : 0);
}
return r;
}
unsigned _m_scale() const
{
nana::rectangle r = _m_bar_area();
return ((this->DirHorizontal == attr_.dir ? r.width : r.height) - attr_.border * 2);
}
double _m_evaluate_by_seekdir(double pos) const
{
switch(attr_.skdir)
{
case seekdir::backward:
if(pos < attr_.pos)
pos = attr_.pos;
break;
case seekdir::forward:
if(pos > attr_.pos)
pos = attr_.pos;
break;
default:
break;
}
return pos;
}
int _m_slider_refpos() const
{
return static_cast<int>(attr_.slider_scale / 2);
}
int _m_slider_pos() const
{
return static_cast<int>(_m_scale() * attr_.vcur / attr_.vmax);
}
unsigned _m_mk_slider_value_by_pos()
{
if(_m_scale())
{
auto cmpvalue = attr_.vcur;
attr_.vcur = static_cast<unsigned>(attr_.pos * attr_.vmax / _m_scale());
if (cmpvalue != attr_.vcur)
_m_emit_value_changed();
}
return attr_.vcur;
}
int _m_mk_slider_pos_by_value()
{
attr_.pos = double(_m_scale()) * attr_.vcur / attr_.vmax;
if(slider_state_.trace == slider_state_.TraceNone)
attr_.adorn_pos = attr_.pos;
return static_cast<int>(attr_.pos);
}
unsigned _m_value_by_pos(double pos) const
{
if(_m_scale())
return static_cast<int>(pos * attr_.vmax / _m_scale());
return 0;
}
void _m_draw_objects()
{
renderer::bar_t bar;
bar.horizontal = (this->DirHorizontal == attr_.dir);
bar.border_size = attr_.border;
bar.r = _m_bar_area();
if (bar.r.empty())
return;
proto_.renderer->bar(other_.wd, *other_.graph, bar);
//adorn
renderer::adorn_t adorn;
adorn.horizontal = bar.horizontal;
adorn.bound.x = (bar.horizontal ? bar.r.x : bar.r.y) + attr_.border;
adorn.bound.y = adorn.bound.x + static_cast<int>(attr_.adorn_pos);
adorn.vcur_scale = static_cast<unsigned>(attr_.pos);
adorn.block = (bar.horizontal ? bar.r.height : bar.r.width) - attr_.border * 2;
adorn.fixedpos = static_cast<int>((bar.horizontal ? bar.r.y : bar.r.x) + attr_.border);
proto_.renderer->adorn(other_.wd, *other_.graph, adorn);
_m_draw_slider();
//adorn textbox
if(proto_.provider && attr_.is_draw_adorn)
{
unsigned vadorn = _m_value_by_pos(attr_.adorn_pos);
nana::string str = proto_.provider->adorn_trace(attr_.vmax, vadorn);
if(str.size())
{
nana::rectangle r;
nana::size ts = other_.graph->text_extent_size(str);
ts.width += 6;
ts.height += 2;
r.width = ts.width;
r.height = ts.height;
const int room = static_cast<int>(attr_.adorn_pos);
if(bar.horizontal)
{
r.y = adorn.fixedpos + static_cast<int>(adorn.block - ts.height) / 2;
if(room > static_cast<int>(ts.width + 2))
r.x = room - static_cast<int>(ts.width + 2);
else
r.x = room + 2;
r.x += this->_m_slider_refpos();
}
else
{
r.x = (other_.graph->width() - ts.width) / 2;
if(room > static_cast<int>(ts.height + 2))
r.y = room - static_cast<int>(ts.height + 2);
else
r.y = room + 2;
r.y += this->_m_slider_refpos();
}
proto_.renderer->adorn_textbox(other_.wd, *other_.graph, str, r);
}
}
}
void _m_draw_slider()
{
renderer::slider_t s;
s.pos = static_cast<int>(attr_.pos);
s.horizontal = (this->DirHorizontal == attr_.dir);
s.scale = attr_.slider_scale;
s.border = attr_.border;
proto_.renderer->slider(other_.wd, *other_.graph, s);
}
private:
struct other_tag
{
window wd;
nana::slider * widget;
paint::graphics * graph;
}other_;
struct prototype_tag
{
pat::cloneable<slider::renderer> renderer;
pat::cloneable<slider::provider> provider;
}proto_;
struct attr_tag
{
seekdir skdir;
dir_t dir;
unsigned border;
unsigned vmax;
unsigned vcur;
double pos;
bool is_draw_adorn;
double adorn_pos;
unsigned slider_scale;
}attr_;
struct slider_state_tag
{
enum t{TraceNone, TraceOver, TraceCapture};
t trace; //true if the mouse press on slider.
int snap_pos;
nana::point refpos; //a point for slider when the mouse was clicking on slider.
slider_state_tag(): trace(TraceNone){}
}slider_state_;
};
//class trigger
trigger::trigger()
: impl_(new controller_t)
{}
trigger::~trigger()
{
delete impl_;
}
trigger::controller_t* trigger::ctrl() const
{
return impl_;
}
void trigger::attached(widget_reference widget, graph_reference graph)
{
impl_->attached(static_cast<nana::slider&>(widget), graph);
}
void trigger::detached()
{
impl_->detached();
}
void trigger::refresh(graph_reference)
{
impl_->draw();
}
void trigger::mouse_down(graph_reference, const arg_mouse& arg)
{
controller_t::where_t what = impl_->seek_where(arg.pos.x, arg.pos.y);
if(controller_t::WhereBar == what || controller_t::WhereSlider == what)
{
bool mkdir = impl_->set_slider_pos(arg.pos.x, arg.pos.y);
impl_->set_slider_refpos(arg.pos);
if(mkdir)
{
impl_->draw();
API::lazy_refresh();
}
}
}
void trigger::mouse_up(graph_reference, const arg_mouse&)
{
bool mkdraw = impl_->release_slider();
if(mkdraw)
{
impl_->draw();
API::lazy_refresh();
}
}
void trigger::mouse_move(graph_reference, const arg_mouse& arg)
{
bool mkdraw = false;
if(impl_->if_trace_slider())
{
mkdraw = impl_->move_slider(arg.pos.x, arg.pos.y);
}
else
{
controller_t::where_t what = impl_->seek_where(arg.pos.x, arg.pos.y);
if(controller_t::WhereNone != what)
mkdraw = impl_->move_adorn(arg.pos.x, arg.pos.y);
else
mkdraw = impl_->reset_adorn();
}
if(mkdraw)
{
impl_->draw();
API::lazy_refresh();
}
}
void trigger::mouse_leave(graph_reference, const arg_mouse&)
{
if(impl_->reset_adorn())
{
impl_->draw();
API::lazy_refresh();
}
}
void trigger::resized(graph_reference, const arg_resized&)
{
impl_->resize();
impl_->draw();
API::lazy_refresh();
}
//end class trigger
}//end namespace slider
}//end namespace drawerbase
//class slider
slider::slider(){}
slider::slider(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
slider::slider(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
void slider::seek(slider::seekdir sd)
{
get_drawer_trigger().ctrl()->seek(sd);
}
void slider::vertical(bool v)
{
get_drawer_trigger().ctrl()->vertical(v);
API::update_window(this->handle());
}
bool slider::vertical() const
{
return get_drawer_trigger().ctrl()->vertical();
}
void slider::vmax(unsigned m)
{
if(this->handle())
{
get_drawer_trigger().ctrl()->vmax(m);
API::update_window(handle());
}
}
unsigned slider::vmax() const
{
if(handle())
return get_drawer_trigger().ctrl()->vmax();
return 0;
}
void slider::value(unsigned v)
{
if(handle())
{
get_drawer_trigger().ctrl()->vcur(v);
API::update_window(handle());
}
}
unsigned slider::value() const
{
if(handle())
return get_drawer_trigger().ctrl()->vcur();
return 0;
}
unsigned slider::move_step(bool forward)
{
if(handle())
{
drawerbase::slider::controller* ctrl = this->get_drawer_trigger().ctrl();
unsigned val = ctrl->move_step(forward);
if(val != ctrl->vcur())
API::update_window(handle());
return val;
}
return 0;
}
unsigned slider::adorn() const
{
if(empty()) return 0;
return get_drawer_trigger().ctrl()->adorn();
}
pat::cloneable<slider::renderer>& slider::ext_renderer()
{
return get_drawer_trigger().ctrl()->ext_renderer();
}
void slider::ext_renderer(const pat::cloneable<slider::renderer>& di)
{
get_drawer_trigger().ctrl()->ext_renderer(di);
}
void slider::ext_provider(const pat::cloneable<slider::provider>& pi)
{
get_drawer_trigger().ctrl()->ext_provider(pi);
}
void slider::transparent(bool enabled)
{
if(enabled)
API::effects_bground(*this, effects::bground_transparent(0), 0.0);
else
API::effects_bground_remove(*this);
}
bool slider::transparent() const
{
return (bground_mode::basic == API::effects_bground_mode(*this));
}
//end class slider
}//end namespace nana

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,515 @@
/*
* A Textbox Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/textbox.hpp
*/
#include <nana/gui/widgets/textbox.hpp>
#include <nana/gui/widgets/skeletons/text_editor.hpp>
#include <stdexcept>
#include <sstream>
namespace nana{ namespace drawerbase {
namespace textbox
{
//class event_agent
event_agent::event_agent(::nana::textbox& wdg)
:widget_(wdg)
{}
void event_agent::first_change()
{
widget_.events().first_change.emit(::nana::arg_textbox{ widget_ });
}
//
//class draweer
drawer::drawer()
: widget_(nullptr), editor_(nullptr)
{
status_.has_focus = false;
}
drawer::text_editor* drawer::editor()
{
return editor_;
}
const drawer::text_editor* drawer::editor() const
{
return editor_;
}
void drawer::set_accept(std::function<bool(nana::char_t)> && fn)
{
pred_acceptive_ = std::move(fn);
}
void drawer::attached(widget_reference wdg, graph_reference graph)
{
auto wd = wdg.handle();
widget_ = &wdg;
evt_agent_.reset(new event_agent(static_cast< ::nana::textbox&>(wdg)));
editor_ = new text_editor(wd, graph);
editor_->textbase().set_event_agent(evt_agent_.get());
editor_->border_renderer([this](graph_reference graph, nana::color_t color){
this->_m_draw_border(graph, color);
});
_m_text_area(graph.width(), graph.height());
API::tabstop(wd);
API::eat_tabstop(wd, true);
API::effects_edge_nimbus(wd, effects::edge_nimbus::active);
API::effects_edge_nimbus(wd, effects::edge_nimbus::over);
}
void drawer::detached()
{
delete editor_;
editor_ = nullptr;
}
void drawer::refresh(graph_reference graph)
{
editor_->render(status_.has_focus);
}
void drawer::focus(graph_reference graph, const arg_focus& arg)
{
status_.has_focus = arg.getting;
refresh(graph);
editor_->show_caret(status_.has_focus);
editor_->reset_caret();
API::lazy_refresh();
}
void drawer::mouse_down(graph_reference, const arg_mouse& arg)
{
if(editor_->mouse_down(arg.left_button, arg.pos))
API::lazy_refresh();
}
void drawer::mouse_move(graph_reference, const arg_mouse& arg)
{
if(editor_->mouse_move(arg.left_button, arg.pos))
API::lazy_refresh();
}
void drawer::mouse_up(graph_reference graph, const arg_mouse& arg)
{
if(editor_->mouse_up(arg.left_button, arg.pos))
API::lazy_refresh();
}
void drawer::mouse_wheel(graph_reference, const arg_wheel& arg)
{
if(editor_->scroll(arg.upwards, true))
{
editor_->reset_caret();
API::lazy_refresh();
}
}
void drawer::mouse_enter(graph_reference, const arg_mouse&)
{
if(editor_->mouse_enter(true))
API::lazy_refresh();
}
void drawer::mouse_leave(graph_reference, const arg_mouse&)
{
if(editor_->mouse_enter(false))
API::lazy_refresh();
}
void drawer::key_press(graph_reference, const arg_keyboard& arg)
{
if(editor_->move(arg.key))
{
editor_->reset_caret();
API::lazy_refresh();
}
}
void drawer::key_char(graph_reference, const arg_keyboard& arg)
{
bool enterable = widget_->enabled() && (!pred_acceptive_ || pred_acceptive_(arg.key));
if (editor_->respone_keyboard(arg.key, enterable))
API::lazy_refresh();
}
void drawer::resized(graph_reference graph, const arg_resized& arg)
{
_m_text_area(arg.width, arg.height);
refresh(graph);
API::lazy_refresh();
}
void drawer::typeface_changed(graph_reference graph)
{
editor_->typeface_changed();
refresh(graph);
API::update_window(widget_->handle());
}
void drawer::_m_text_area(unsigned width, unsigned height)
{
if(editor_)
{
nana::rectangle r(0, 0, width, height);
if (!API::widget_borderless(widget_->handle()))
{
r.x = r.y = 2;
r.width = (width > 4 ? width - 4 : 0);
r.height = (height > 4 ? height - 4 : 0);
}
editor_->text_area(r);
}
}
void drawer::_m_draw_border(graph_reference graph, nana::color_t bgcolor)
{
if (!API::widget_borderless(widget_->handle()))
{
nana::rectangle r(graph.size());
graph.rectangle(r, (status_.has_focus ? 0x0595E2 : 0x999A9E), false);
graph.rectangle(r.pare_off(1), bgcolor, false);
}
}
//end class drawer
}//end namespace textbox
}//end namespace drawerbase
//class textbox
textbox::textbox()
{}
textbox::textbox(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
textbox::textbox(window wd, const nana::string& text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
textbox::textbox(window wd, const nana::char_t* text, bool visible)
{
create(wd, rectangle(), visible);
caption(text);
}
textbox::textbox(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
void textbox::load(nana::string file)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor && editor->load(file.data()))
API::update_window(handle());
}
void textbox::store(nana::string file)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor)
editor->textbase().store(std::move(file));
}
void textbox::store(nana::string file, nana::unicode encoding)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor)
editor->textbase().store(std::move(file), encoding);
}
textbox& textbox::reset(nana::string str)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor)
{
editor->text(std::move(str));
editor->textbase().reset();
API::update_window(this->handle());
}
return *this;
}
nana::string textbox::filename() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
return editor->textbase().filename();
return{};
}
bool textbox::edited() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->textbase().edited() : false);
}
textbox& textbox::edited_reset()
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor)
editor->textbase().edited_reset();
return *this;
}
bool textbox::saved() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->textbase().saved() : false);
}
bool textbox::getline(std::size_t line_index, nana::string& text) const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->getline(line_index, text) : false);
}
textbox& textbox::append(const nana::string& text, bool at_caret)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
{
if(at_caret == false)
editor->move_caret_end();
editor->put(text);
API::update_window(this->handle());
}
return *this;
}
/// Determine wheter the text is auto-line changed.
bool textbox::line_wrapped() const
{
internal_scope_guard lock;
return get_drawer_trigger().editor()->line_wrapped();
}
textbox& textbox::line_wrapped(bool autl)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor->line_wrapped(autl))
editor->render(API::is_focus_window(handle()));
return *this;
}
bool textbox::multi_lines() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->attr().multi_lines : false);
}
textbox& textbox::multi_lines(bool ml)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor && editor->multi_lines(ml))
API::update_window(handle());
return *this;
}
bool textbox::editable() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->attr().editable : false);
}
textbox& textbox::editable(bool able)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
editor->editable(able);
return *this;
}
void textbox::set_accept(std::function<bool(nana::char_t)> fn)
{
internal_scope_guard lock;
get_drawer_trigger().set_accept(std::move(fn));
}
textbox& textbox::tip_string(nana::string str)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor && editor->tip_string(std::move(str)))
API::refresh_window(handle());
return *this;
}
textbox& textbox::mask(nana::char_t ch)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor && editor->mask(ch))
API::refresh_window(handle());
return *this;
}
bool textbox::selected() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->selected() : false);
}
void textbox::select(bool yes)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor && editor->select(yes))
API::refresh_window(*this);
}
void textbox::copy() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
editor->copy();
}
void textbox::paste()
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
{
editor->paste();
API::refresh_window(*this);
}
}
void textbox::del()
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if(editor)
{
editor->del();
API::refresh_window(*this);
}
}
int textbox::to_int() const
{
nana::string s = _m_caption();
if (s.empty()) return 0;
#ifdef NANA_UNICODE
std::wstringstream ss;
#else
std::stringstream ss;
#endif
int value;
ss << s;
ss >> value;
return value;
}
double textbox::to_double() const
{
nana::string s = _m_caption();
if (s.empty()) return 0;
#ifdef NANA_UNICODE
std::wstringstream ss;
#else
std::stringstream ss;
#endif
double value;
ss << s;
ss >> value;
return value;
}
textbox& textbox::from(int n)
{
#ifdef NANA_UNICODE
std::wstringstream ss;
#else
std::stringstream ss;
#endif
ss << n;
_m_caption(ss.str());
return *this;
}
textbox& textbox::from(double d)
{
#ifdef NANA_UNICODE
std::wstringstream ss;
#else
std::stringstream ss;
#endif
ss << d;
_m_caption(ss.str());
return *this;
}
//Override _m_caption for caption()
nana::string textbox::_m_caption() const
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
return (editor ? editor->text() : nana::string());
}
void textbox::_m_caption(nana::string&& str)
{
internal_scope_guard lock;
auto editor = get_drawer_trigger().editor();
if (editor)
{
editor->text(std::move(str));
API::update_window(this->handle());
}
}
//Override _m_typeface for changing the caret
void textbox::_m_typeface(const nana::paint::font& font)
{
widget::_m_typeface(font);
auto editor = get_drawer_trigger().editor();
if(editor)
editor->reset_caret_height();
}
//end class textbox
}//end namespace nana

View File

@@ -0,0 +1,514 @@
/*
* A Toolbar Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/toolbar.cpp
*/
#include <nana/gui/widgets/toolbar.hpp>
#include <vector>
#include <stdexcept>
#include <nana/gui/tooltip.hpp>
namespace nana
{
namespace drawerbase
{
namespace toolbar
{
struct listitem
{
nana::string text;
nana::paint::image image;
bool enable;
};
struct item_type
{
enum{TypeButton, TypeContainer};
typedef std::size_t size_type;
nana::string text;
nana::paint::image image;
unsigned pixels;
nana::size textsize;
bool enable;
window other;
int type;
std::function<void(size_type, size_type)> answer;
std::vector<listitem> children;
item_type(const nana::string& text, const nana::paint::image& img, int type)
:text(text), image(img), pixels(0), enable(true), other(nullptr), type(type)
{}
};
class container
{
container(const container&);
container& operator=(const container&);
public:
typedef std::vector<item_type*>::size_type size_type;
typedef std::vector<item_type*>::iterator iterator;
typedef std::vector<item_type*>::const_iterator const_iterator;
container()
{}
~container()
{
for(auto ptr : cont_)
delete ptr;
}
void insert(size_type pos, const nana::string& text, const nana::paint::image& img, int type)
{
item_type* m = new item_type(text, img, type);
if(pos < cont_.size())
cont_.insert(cont_.begin() + pos, m);
else
cont_.push_back(m);
}
void push_back(const nana::string& text, const nana::paint::image& img)
{
insert(cont_.size(), text, img, item_type::TypeButton);
}
void push_back(const nana::string& text)
{
insert(cont_.size(), text, nana::paint::image(), item_type::TypeButton);
}
void insert(size_type pos)
{
if(pos < cont_.size())
cont_.insert(cont_.begin() + pos, static_cast<item_type*>(nullptr)); //both works in C++0x and C++2003
else
cont_.push_back(nullptr);
}
void push_back()
{
cont_.push_back(nullptr);
}
size_type size() const
{
return cont_.size();
}
item_type* at(size_type n)
{
if(n < cont_.size())
return cont_[n];
throw std::out_of_range("toolbar: bad index!");
}
iterator begin()
{
return cont_.begin();
}
iterator end()
{
return cont_.end();
}
const_iterator begin() const
{
return cont_.cbegin();
}
const_iterator end() const
{
return cont_.cend();
}
private:
std::vector<item_type*> cont_;
};
class item_renderer
{
public:
enum class state_t{normal, highlighted, selected};
const static unsigned extra_size = 6;
item_renderer(nana::paint::graphics& graph, bool textout, unsigned scale, nana::color_t color)
:graph(graph), textout(textout), scale(scale), color(color)
{}
void operator()(int x, int y, unsigned width, unsigned height, item_type& item, state_t state)
{
//draw background
if(state != state_t::normal)
graph.rectangle(x, y, width, height, 0x3399FF, false);
switch(state)
{
case state_t::highlighted:
graph.shadow_rectangle(x + 1, y + 1, width - 2, height - 2, color, /*graph.mix(color, 0xC0DDFC, 0.5)*/ 0xC0DDFC, true);
break;
case state_t::selected:
graph.shadow_rectangle(x + 1, y + 1, width - 2, height - 2, color, /*graph.mix(color, 0x99CCFF, 0.5)*/0x99CCFF, true);
default: break;
}
if(item.image.empty() == false)
{
nana::size size = item.image.size();
if(size.width > scale) size.width = scale;
if(size.height > scale) size.height = scale;
nana::point pos(x, y);
pos.x += static_cast<int>(scale + extra_size - size.width) / 2;
pos.y += static_cast<int>(height - size.height) / 2;
item.image.paste(size, graph, pos);
if(item.enable == false)
{
nana::paint::graphics gh(size.width, size.height);
gh.bitblt(size, graph, pos);
gh.rgb_to_wb();
gh.paste(graph, pos.x, pos.y);
}
else if(state == state_t::normal)
graph.blend(nana::rectangle(pos, size), graph.mix(color, 0xC0DDFC, 0.5), 0.25);
x += scale;
width -= scale;
}
if(textout)
{
graph.string(x + (width - item.textsize.width) / 2, y + (height - item.textsize.height) / 2, 0x0, item.text);
}
}
protected:
nana::paint::graphics& graph;
bool textout;
unsigned scale;
nana::color_t color;
};
struct drawer::drawer_impl_type
{
event_handle event_size;
unsigned scale;
bool textout;
size_type which;
item_renderer::state_t state;
container cont;
nana::tooltip tooltip;
drawer_impl_type()
: event_size(nullptr),
scale(16),
textout(false),
which(npos),
state(item_renderer::state_t::normal)
{}
};
//class drawer
drawer::drawer()
: impl_(new drawer_impl_type)
{
}
drawer::~drawer()
{
delete impl_;
}
void drawer::append(const nana::string& text, const nana::paint::image& img)
{
impl_->cont.push_back(text, img);
}
void drawer::append()
{
impl_->cont.push_back();
}
bool drawer::enable(drawer::size_type n) const
{
if(impl_->cont.size() > n)
{
auto item = impl_->cont.at(n);
return (item && item->enable);
}
return false;
}
bool drawer::enable(size_type n, bool eb)
{
if(impl_->cont.size() > n)
{
item_type * item = impl_->cont.at(n);
if(item && (item->enable != eb))
{
item->enable = eb;
return true;
}
}
return false;
}
void drawer::scale(unsigned s)
{
impl_->scale = s;
for(auto m : impl_->cont)
_m_fill_pixels(m, true);
}
void drawer::refresh(graph_reference)
{
_m_draw();
}
void drawer::attached(widget_reference widget, graph_reference graph)
{
graph_ = &graph;
widget_ = static_cast< ::nana::toolbar*>(&widget);
widget.caption(STR("Nana Toolbar"));
impl_->event_size = widget.events().resized.connect(std::bind(&drawer::_m_owner_sized, this, std::placeholders::_1));
}
void drawer::detached()
{
API::umake_event(impl_->event_size);
impl_->event_size = nullptr;
}
void drawer::mouse_move(graph_reference graph, const arg_mouse& arg)
{
if(arg.left_button == false)
{
size_type which = _m_which(arg.pos.x, arg.pos.y, true);
if(impl_->which != which)
{
if (impl_->which != npos && impl_->cont.at(impl_->which)->enable)
{
::nana::arg_toolbar arg{ *widget_, impl_->which };
widget_->events().leave.emit(arg);
}
impl_->which = which;
if(which == npos || impl_->cont.at(which)->enable)
{
impl_->state = (arg.left_button ? item_renderer::state_t::selected : item_renderer::state_t::highlighted);
_m_draw();
API::lazy_refresh();
if (impl_->state == item_renderer::state_t::highlighted)
{
::nana::arg_toolbar arg{ *widget_, which };
widget_->events().enter.emit(arg);
}
}
if(which != npos)
impl_->tooltip.show(widget_->handle(), nana::point(arg.pos.x, arg.pos.y + 20), (*(impl_->cont.begin() + which))->text, 0);
else
impl_->tooltip.close();
}
}
}
void drawer::mouse_leave(graph_reference, const arg_mouse&)
{
if(impl_->which != npos)
{
size_type which = impl_->which;
impl_->which = npos;
_m_draw();
API::lazy_refresh();
if (which != npos && impl_->cont.at(which)->enable)
{
::nana::arg_toolbar arg{ *widget_, which };
widget_->events().leave.emit(arg);
}
}
impl_->tooltip.close();
}
void drawer::mouse_down(graph_reference, const arg_mouse&)
{
impl_->tooltip.close();
if(impl_->which != npos && (impl_->cont.at(impl_->which)->enable))
{
impl_->state = item_renderer::state_t::selected;
_m_draw();
API::lazy_refresh();
}
}
void drawer::mouse_up(graph_reference, const arg_mouse& arg)
{
if(impl_->which != npos)
{
size_type which = _m_which(arg.pos.x, arg.pos.y, false);
if(impl_->which == which)
{
::nana::arg_toolbar arg{ *widget_, which };
widget_->events().selected.emit(arg);
impl_->state = item_renderer::state_t::highlighted;
}
else
{
impl_->which = which;
impl_->state = (which == npos ? item_renderer::state_t::normal : item_renderer::state_t::highlighted);
}
_m_draw();
API::lazy_refresh();
}
}
drawer::size_type drawer::_m_which(int x, int y, bool want_if_disabled) const
{
if(x < 2 || y < 2 || y >= static_cast<int>(impl_->scale + item_renderer::extra_size + 2)) return npos;
x -= 2;
size_type pos = 0;
for(auto m: impl_->cont)
{
bool compart = (nullptr == m);
if(x < static_cast<int>(compart ? 3 : m->pixels))
return ((compart || (m->enable == false && want_if_disabled == false)) ? npos : pos);
x -= (compart ? 3 : m->pixels);
++pos;
}
return npos;
}
void drawer::_m_draw_background(nana::color_t color)
{
graph_->shadow_rectangle(graph_->size(), graph_->mix(color, 0xFFFFFF, 0.9), graph_->mix(color, 0x0, 0.95), true);
}
void drawer::_m_draw()
{
int x = 2, y = 2;
unsigned color = API::background(widget_->handle());
_m_draw_background(color);
item_renderer ir(*graph_, impl_->textout, impl_->scale, color);
size_type index = 0;
for(auto item : impl_->cont)
{
if(item)
{
_m_fill_pixels(item, false);
ir(x, y, item->pixels, impl_->scale + ir.extra_size, *item, (index == impl_->which ? impl_->state : item_renderer::state_t::normal));
x += item->pixels;
}
else
{
graph_->line(x + 2, y + 2, x + 2, y + impl_->scale + ir.extra_size - 4, 0x808080);
x += 6;
}
++index;
}
}
void drawer::_m_owner_sized(const arg_resized& arg)
{
auto wd = widget_->handle();
API::window_size(wd, nana::size(arg.width, widget_->size().height));
_m_draw();
API::update_window(wd);
}
void drawer::_m_fill_pixels(item_type* item, bool force)
{
if(item && (force || (0 == item->pixels)))
{
if(item->text.size())
item->textsize = graph_->text_extent_size(item->text);
if(item->image.empty() == false)
item->pixels = impl_->scale + item_renderer::extra_size;
if(item->textsize.width && impl_->textout)
item->pixels += item->textsize.width + 8;
}
}
//};//class drawer
}//end namespace toolbar
}//end namespace drawerbase
//class toolbar
toolbar::toolbar()
{}
toolbar::toolbar(window wd, bool visible)
{
create(wd, rectangle(), visible);
}
toolbar::toolbar(window wd, const rectangle& r, bool visible)
{
create(wd, r, visible);
}
void toolbar::append()
{
get_drawer_trigger().append();
API::refresh_window(handle());
}
void toolbar::append(const nana::string& text, const nana::paint::image& img)
{
get_drawer_trigger().append(text, img);
API::refresh_window(handle());
}
void toolbar::append(const nana::string& text)
{
get_drawer_trigger().append(text, nana::paint::image());
API::refresh_window(this->handle());
}
bool toolbar::enable(size_type n) const
{
return get_drawer_trigger().enable(n);
}
void toolbar::enable(size_type n, bool eb)
{
if(get_drawer_trigger().enable(n, eb))
API::refresh_window(this->handle());
}
void toolbar::scale(unsigned s)
{
get_drawer_trigger().scale(s);
API::refresh_window(handle());
}
//}; class toolbar
}//end namespace nana

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,286 @@
/*
* The fundamental widget class implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/widget.cpp
*/
#include <nana/gui/widgets/widget.hpp>
#include <nana/gui/tooltip.hpp>
namespace nana
{
namespace internationalization_parts
{
void set_eval(window, i18n_eval&&);
}
//class widget
//@brief:The definition of class widget
widget::~widget(){}
nana::string widget::caption() const
{
return this->_m_caption();
}
void widget::caption(nana::string str)
{
_m_caption(std::move(str));
}
void widget::i18n(i18n_eval eval)
{
if (handle())
{
_m_caption(eval());
internationalization_parts::set_eval(handle(), std::move(eval));
}
}
nana::cursor widget::cursor() const
{
return _m_cursor();
}
void widget::cursor(nana::cursor cur)
{
_m_cursor(cur);
}
void widget::typeface(const nana::paint::font& font)
{
_m_typeface(font);
}
nana::paint::font widget::typeface() const
{
return _m_typeface();
}
void widget::close()
{
_m_close();
}
window widget::parent() const
{
return API::get_parent_window(handle());
}
bool widget::enabled() const
{
return API::window_enabled(handle());
}
void widget::enabled(bool value)
{
_m_enabled(value);
}
void widget::enable_dropfiles(bool enb)
{
API::enable_dropfiles(handle(), enb);
}
bool widget::empty() const
{
return (nullptr == handle());
}
void widget::focus()
{
API::focus_window(handle());
}
bool widget::focused() const
{
return API::is_focus_window(handle());
}
void widget::show()
{
_m_show(true);
}
void widget::hide()
{
_m_show(false);
}
bool widget::visible() const
{
return _m_visible();
}
nana::size widget::size() const
{
return API::window_size(handle());
}
void widget::size(const nana::size& sz)
{
_m_size(sz);
}
nana::point widget::pos() const
{
return API::window_position(handle());
}
void widget::move(int x, int y)
{
_m_move(x, y);
}
void widget::move(const rectangle& r)
{
_m_move(r);
}
void widget::foreground(nana::color_t value)
{
_m_foreground(value);
}
nana::color_t widget::foreground() const
{
return _m_foreground();
}
void widget::background(nana::color_t value)
{
_m_background(value);
}
nana::color_t widget::background() const
{
return _m_background();
}
general_events& widget::events() const
{
return _m_get_general_events();
}
void widget::umake_event(event_handle eh) const
{
API::umake_event(eh);
}
widget& widget::tooltip(const nana::string& text)
{
nana::tooltip::set(*this, text);
return *this;
}
widget::operator widget::dummy_bool_type() const
{
return (handle()? dummy_bool_type(1):0);
}
widget::operator window() const
{
return handle();
}
void widget::_m_complete_creation()
{}
nana::string widget::_m_caption() const
{
return API::dev::window_caption(handle());
}
void widget::_m_caption(nana::string&& str)
{
API::dev::window_caption(handle(), std::move(str));
}
nana::cursor widget::_m_cursor() const
{
return API::window_cursor(handle());
}
void widget::_m_cursor(nana::cursor cur)
{
API::window_cursor(handle(), cur);
}
void widget::_m_close()
{
API::close_window(handle());
}
bool widget::_m_enabled() const
{
return API::window_enabled(handle());
}
void widget::_m_enabled(bool value)
{
API::window_enabled(handle(), value);
}
bool widget::_m_show(bool visible)
{
API::show_window(handle(), visible);
return visible;
}
bool widget::_m_visible() const
{
return API::visible(handle());
}
void widget::_m_size(const nana::size& sz)
{
API::window_size(handle(), sz);
}
void widget::_m_move(int x, int y)
{
API::move_window(handle(), x, y);
}
void widget::_m_move(const rectangle& r)
{
API::move_window(handle(), r);
}
void widget::_m_typeface(const paint::font& font)
{
API::typeface(handle(), font);
}
nana::paint::font widget::_m_typeface() const
{
return API::typeface(handle());
}
void widget::_m_foreground(nana::color_t value)
{
API::foreground(handle(), value);
}
nana::color_t widget::_m_foreground() const
{
return API::foreground(handle());
}
void widget::_m_background(nana::color_t value)
{
API::background(handle(), value);
}
nana::color_t widget::_m_background() const
{
return API::background(handle());
}
//end class widget
}//end namespace nana

22
source/gui/wvl.cpp Normal file
View File

@@ -0,0 +1,22 @@
/*
* Nana GUI Library Definition
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/wvl.cpp
* @description:
* the file contains the files required for running of Nana.GUI
*/
#include <nana/gui/wvl.hpp>
namespace nana
{
void exec()
{
detail::bedrock::instance().pump_event(nullptr, false);
}
}//end namespace nana

View File

@@ -0,0 +1,500 @@
/*
* An Implementation of i18n
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/internationalization.cpp
*/
#include <nana/internationalization.hpp>
#include <nana/gui/widgets/widget.hpp>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <memory>
namespace nana
{
namespace internationalization_parts
{
enum class token
{
msgid, msgstr, string, eof
};
//Forward declaration
void use_eval();
class tokenizer
{
public:
tokenizer(const std::string& file)
{
std::ifstream ifs(file.data(), std::ios::binary);
if (ifs)
{
ifs.seekg(0, std::ios::end);
auto len = static_cast<unsigned>(ifs.tellg());
ifs.seekg(0, std::ios::beg);
if (len > 0)
{
data_.reset(new char[len]);
ifs.read(data_.get(), len);
read_ptr_ = data_.get();
end_ptr_ = read_ptr_ + len;
}
}
}
token read()
{
if (read_ptr_ == end_ptr_)
return token::eof;
str_.clear();
_m_eat_ws();
if (*read_ptr_ == '"')
{
bool reach_right_quota;
while (true)
{
reach_right_quota = false;
bool escape = false;
for (auto i = read_ptr_ + 1; i != end_ptr_; ++i)
{
if (escape)
{
escape = false;
continue;
}
if ('"' == *i)
{
str_.append(read_ptr_ + 1, i - read_ptr_ - 1);
read_ptr_ = i + 1;
reach_right_quota = true;
break;
}
}
_m_eat_ws();
if (read_ptr_ == end_ptr_ || '"' != *read_ptr_)
break;
}
if (reach_right_quota)
return token::string;
}
else if ('a' <= *read_ptr_ && *read_ptr_ <= 'z')
{
for (auto i = read_ptr_; i != end_ptr_; ++i)
{
if (*i < 'a' || 'z' < *i)
{
std::string id(read_ptr_, i);
read_ptr_ = i;
if (id == "msgid")
return token::msgid;
else if (id == "msgstr")
return token::msgstr;
break;
}
}
read_ptr_ = end_ptr_;
}
return token::eof;
}
std::string& get_str()
{
return str_;
}
private:
void _m_eat_ws()
{
for (auto i = read_ptr_; i != end_ptr_; ++i)
{
switch (*i)
{
case ' ': case '\t': case '\r': case '\n':
break;
default:
read_ptr_ = i;
return;
}
}
read_ptr_ = end_ptr_;
}
private:
std::unique_ptr<char[]> data_;
const char * read_ptr_{ nullptr };
const char * end_ptr_{ nullptr };
std::string str_;
};//end class tokenizer
struct data
{
std::unordered_map<std::string, nana::string> table;
};
static std::shared_ptr<data>& get_data_ptr()
{
static std::shared_ptr<data> data_ptr;
return data_ptr;
}
void load(const std::string& file, bool utf8)
{
auto impl = std::make_shared<data>();
tokenizer tknizer(file);
while (true)
{
if (token::msgid != tknizer.read())
break;
if (token::string != tknizer.read())
return;
std::string msgid = std::move(tknizer.get_str());
if (!utf8)
msgid = nana::charset(std::move(msgid)).to_bytes(nana::unicode::utf8);
if (token::msgstr != tknizer.read())
return;
if (token::string != tknizer.read())
return;
nana::string str;
if (utf8)
str = nana::charset(std::move(tknizer.get_str()), nana::unicode::utf8);
else
str = nana::charset(std::move(tknizer.get_str()));
nana::string::size_type pos = 0;
while (true)
{
pos = str.find('\\', pos);
if (pos == str.npos)
break;
if (pos + 1 < str.size())
{
auto ch = str[pos + 1];
switch (ch)
{
case '"': case '\\':
str.erase(pos, 1);
break;
case 'n':
str.erase(pos, 1);
str[pos] = '\n';
break;
case 't':
str.erase(pos, 1);
str[pos] = '\t';
break;
}
++pos;
}
else
break;
}
impl->table[std::move(msgid)].swap(str);
}
get_data_ptr().swap(impl);
use_eval();
}
struct eval_window
{
nana::event_handle destroy{nullptr};
i18n_eval eval;
eval_window() = default;
eval_window(i18n_eval&& arg)
: eval(std::move(arg))
{}
};
struct eval_manager
{
std::recursive_mutex mutex;
std::map<window, eval_window> table;
};
eval_manager& get_eval_manager()
{
static eval_manager evals;
return evals;
}
void set_eval(nana::window wd, i18n_eval&& eval)
{
auto & mgr = get_eval_manager();
std::lock_guard<std::recursive_mutex> lock(mgr.mutex);
auto i = mgr.table.find(wd);
if (i == mgr.table.end())
{
auto result = mgr.table.emplace(wd, std::move(eval));
result.first->second.destroy = nana::API::events(wd).destroy([wd]{
auto & mgr = get_eval_manager();
std::lock_guard<std::recursive_mutex> lock(mgr.mutex);
mgr.table.erase(wd);
});
}
else
i->second.eval = std::move(eval);
}
void use_eval()
{
auto & mgr = get_eval_manager();
std::lock_guard<std::recursive_mutex> lock(mgr.mutex);
for (auto & eval : mgr.table)
{
nana::API::window_caption(eval.first, eval.second.eval());
}
}
}//end namespace internationalization_parts
void internationalization::load(const std::string& file)
{
internationalization_parts::load(file, false);
}
void internationalization::load_utf8(const std::string& file)
{
internationalization_parts::load(file, true);
}
nana::string internationalization::get(std::string msgid) const
{
nana::string str;
if(_m_get(msgid, str))
_m_replace_args(str, nullptr);
return str;
}
void internationalization::set(std::string msgid, nana::string msgstr)
{
auto & ptr = internationalization_parts::get_data_ptr();
if (!ptr)
ptr = std::make_shared<internationalization_parts::data>();
ptr->table[msgid].swap(msgstr);
}
bool internationalization::_m_get(std::string& msgid, nana::string& msgstr) const
{
auto impl = internationalization_parts::get_data_ptr();
if (impl)
{
auto i = impl->table.find(msgid);
if (i != impl->table.end())
{
msgstr = i->second;
return true;
}
}
msgstr = nana::charset(std::move(msgid), nana::unicode::utf8);
return false;
}
void internationalization::_m_replace_args(nana::string& str, std::vector<nana::string> * arg_strs) const
{
nana::string::size_type offset = 0;
while (true)
{
auto pos = str.find(L"%arg", offset);
if (pos == str.npos)
break;
offset = pos;
pos = str.find_first_not_of(L"0123456789", offset + 4);
if ((pos == str.npos) || (pos != offset + 4))
{
nana::string::size_type erase_n = 0;
nana::string::size_type arg_n = str.npos;
if (pos != str.npos)
{
erase_n = pos - offset;
arg_n = pos - offset - 4;
}
else
erase_n = str.size() - offset;
//If there is not a parameter for %argNNN, the %argNNN will be erased.
//a workaround, MinGW does not provide std::stoi
std::wstringstream ss;
std::size_t arg;
ss<<str.substr(offset + 4, arg_n);
ss>>arg;
if (arg_strs && arg < arg_strs->size())
str.replace(offset, erase_n, (*arg_strs)[arg]);
else
str.erase(offset, erase_n);
}
else
offset += 4;
}
}
//end class internationalization
class i18n_eval::arg_string
: public eval_arg
{
public:
arg_string(nana::string str)
: str_(std::move(str))
{}
nana::string eval() const override
{
return str_;
}
std::unique_ptr<eval_arg> clone() const override
{
return std::unique_ptr<eval_arg>(new arg_string(str_));
}
private:
nana::string str_;
};
class i18n_eval::arg_eval
: public eval_arg
{
public:
arg_eval(const i18n_eval& eval)
: eval_{ eval }
{}
arg_eval(i18n_eval&& eval)
: eval_{ std::move(eval) }
{}
nana::string eval() const override
{
return eval_();
}
std::unique_ptr<eval_arg> clone() const override
{
return std::unique_ptr<eval_arg>(new arg_eval(eval_));
}
private:
i18n_eval eval_;
};
//class i18n_eval
i18n_eval::i18n_eval(const i18n_eval& rhs)
: msgid_(rhs.msgid_)
{
for (auto & arg : rhs.args_)
args_.emplace_back(arg->clone());
}
//Workaround for VC2013, becuase it can't specified a default explicit move-constructor
i18n_eval::i18n_eval(i18n_eval&& other)
: msgid_(std::move(other.msgid_)), args_(std::move(other.args_))
{
}
i18n_eval& i18n_eval::operator=(const i18n_eval& rhs)
{
if (this != &rhs)
{
msgid_ = rhs.msgid_;
for (auto & arg : rhs.args_)
args_.emplace_back(arg->clone());
}
return *this;
}
i18n_eval& i18n_eval::operator=(i18n_eval&& rhs)
{
if (this != &rhs)
{
msgid_ = std::move(rhs.msgid_);
args_ = std::move(rhs.args_);
}
return *this;
}
nana::string i18n_eval::operator()() const
{
if (msgid_.empty())
return{};
std::vector<nana::string> arg_strs;
for (auto & arg : args_)
arg_strs.emplace_back(arg->eval());
internationalization i18n;
std::string msgid = msgid_; //msgid is required to be movable by i18n._m_get
nana::string msgstr;
if (i18n._m_get(msgid, msgstr))
i18n._m_replace_args(msgstr, &arg_strs);
return msgstr;
}
void i18n_eval::_m_add_args(i18n_eval& eval)
{
args_.emplace_back(new arg_eval(eval));
}
void i18n_eval::_m_add_args(const i18n_eval& eval)
{
args_.emplace_back(new arg_eval(eval));
}
void i18n_eval::_m_add_args(i18n_eval&& eval)
{
args_.emplace_back(new arg_eval(std::move(eval)));
}
void i18n_eval::_m_add_args(std::string& str)
{
args_.emplace_back(new arg_string(nana::charset(str)));
}
void i18n_eval::_m_add_args(const std::string& str)
{
args_.emplace_back(new arg_string(nana::charset(str)));
}
void i18n_eval::_m_add_args(std::string&& str)
{
args_.emplace_back(new arg_string(nana::charset(std::move(str))));
}
void i18n_eval::_m_add_args(std::wstring& str)
{
args_.emplace_back(new arg_string(nana::charset(str)));
}
void i18n_eval::_m_add_args(const std::wstring& str)
{
args_.emplace_back(new arg_string(nana::charset(str)));
}
void i18n_eval::_m_add_args(std::wstring&& str)
{
args_.emplace_back(new arg_string(nana::charset(std::move(str))));
}
//end class i18n_eval
}

View File

@@ -0,0 +1,119 @@
#include <nana/paint/detail/image_process_provider.hpp>
#include <nana/paint/detail/image_processor.hpp>
namespace nana
{
namespace paint
{
namespace image_process
{
//There are definitions of pure virtual destructor of image processor interfaces
stretch_interface::~stretch_interface(){}
alpha_blend_interface::~alpha_blend_interface(){}
blend_interface::~blend_interface(){}
line_interface::~line_interface(){}
blur_interface::~blur_interface(){}
}
namespace detail
{
//class image_process_provider
image_process_provider& image_process_provider::instance()
{
static image_process_provider obj;
return obj;
}
image_process_provider::image_process_provider()
{
add<paint::detail::algorithms::bilinear_interoplation>(stretch_, "bilinear interoplation");
add<paint::detail::algorithms::proximal_interoplation>(stretch_, "proximal interoplation");
add<paint::detail::algorithms::alpha_blend>(alpha_blend_, "alpha_blend");
add<paint::detail::algorithms::blend>(blend_, "blend");
add<paint::detail::algorithms::bresenham_line>(line_, "bresenham_line");
add<paint::detail::algorithms::superfast_blur>(blur_, "superfast_blur");
}
image_process_provider::stretch_tag& image_process_provider::ref_stretch_tag()
{
return stretch_;
}
paint::image_process::stretch_interface * const * image_process_provider::stretch() const
{
return &stretch_.employee;
}
paint::image_process::stretch_interface * image_process_provider::ref_stretch(const std::string& name) const
{
return _m_read(stretch_, name);
}
//alpha_blend
image_process_provider::alpha_blend_tag& image_process_provider::ref_alpha_blend_tag()
{
return alpha_blend_;
}
paint::image_process::alpha_blend_interface * const * image_process_provider::alpha_blend() const
{
return &alpha_blend_.employee;
}
paint::image_process::alpha_blend_interface * image_process_provider::ref_alpha_blend(const std::string& name) const
{
return _m_read(alpha_blend_, name);
}
//blend
image_process_provider::blend_tag& image_process_provider::ref_blend_tag()
{
return blend_;
}
paint::image_process::blend_interface * const * image_process_provider::blend() const
{
return &blend_.employee;
}
paint::image_process::blend_interface * image_process_provider::ref_blend(const std::string& name) const
{
return _m_read(blend_, name);
}
//line
image_process_provider::line_tag & image_process_provider::ref_line_tag()
{
return line_;
}
paint::image_process::line_interface * const * image_process_provider::line() const
{
return &line_.employee;
}
paint::image_process::line_interface * image_process_provider::ref_line(const std::string& name) const
{
return _m_read(line_, name);
}
//Blur
image_process_provider::blur_tag & image_process_provider::ref_blur_tag()
{
return blur_;
}
paint::image_process::blur_interface * const * image_process_provider::blur() const
{
return &blur_.employee;
}
paint::image_process::blur_interface * image_process_provider::ref_blur(const std::string& name) const
{
return _m_read(blur_, name);
}
//end class image_process_provider
}
}
}//end namespace nana

View File

@@ -0,0 +1,230 @@
/*
* Platform Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/paint/detail/native_paint_interface.cpp
*/
#include <nana/config.hpp>
#include PLATFORM_SPEC_HPP
#include <nana/paint/detail/native_paint_interface.hpp>
#include <nana/paint/pixel_buffer.hpp>
#include <nana/gui/layout_utility.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_X11)
#include <X11/Xlib.h>
#endif
namespace nana
{
namespace paint
{
namespace detail
{
nana::size drawable_size(drawable_type dw)
{
if(0 == dw) return nana::size();
#if defined(NANA_WINDOWS)
BITMAP bmp;
::GetObject(dw->pixmap, sizeof bmp, &bmp);
return nana::size(bmp.bmWidth, bmp.bmHeight);
#elif defined(NANA_X11)
nana::detail::platform_spec & spec = nana::detail::platform_spec::instance();
Window root;
int x, y;
unsigned width, height;
unsigned border, depth;
nana::detail::platform_scope_guard psg;
::XGetGeometry(spec.open_display(), dw->pixmap, &root, &x, &y, &width, &height, &border, &depth);
return nana::size(width, height);
#endif
}
unsigned char * alloc_fade_table(double fade_rate)
{
unsigned char* tablebuf = new unsigned char[0x100 * 2];
unsigned char* d_table = tablebuf;
unsigned char* s_table = d_table + 0x100;
double fade_rate_mul_to_add = 0;
double fade_rate_2 = fade_rate + fade_rate;
double fade_rate_3 = fade_rate_2 + fade_rate;
double fade_rate_4 = fade_rate_3 + fade_rate;
for(int i = 0; i < 0x100; i += 4, fade_rate_mul_to_add += fade_rate_4)
{
d_table[0] = static_cast<unsigned char>(fade_rate_mul_to_add);
s_table[0] = i - d_table[0];
d_table[1] = static_cast<unsigned char>(fade_rate_mul_to_add + fade_rate);
s_table[1] = i + 1 - d_table[1];
d_table[2] = static_cast<unsigned char>(fade_rate_mul_to_add + fade_rate_2);
s_table[2] = i + 2 - d_table[2];
d_table[3] = static_cast<unsigned char>(fade_rate_mul_to_add + fade_rate_3);
s_table[3] = i + 3 - d_table[3];
d_table += 4;
s_table += 4;
}
return tablebuf;
}
void free_fade_table(const unsigned char* table)
{
delete [] table;
}
nana::pixel_rgb_t fade_color(nana::pixel_rgb_t bgcolor, nana::pixel_rgb_t fgcolor, double fade_rate)
{
pixel_rgb_t ret;
double lrate = 1.0 - fade_rate;
ret.u.element.red = static_cast<unsigned char>(bgcolor.u.element.red * fade_rate + fgcolor.u.element.red * lrate);
ret.u.element.green = static_cast<unsigned char>(bgcolor.u.element.green * fade_rate + fgcolor.u.element.green * lrate);
ret.u.element.blue = static_cast<unsigned char>(bgcolor.u.element.blue * fade_rate + fgcolor.u.element.blue * lrate);
ret.u.element.alpha_channel = 0;
return ret;
}
nana::pixel_rgb_t fade_color(nana::pixel_rgb_t bgcolor, nana::pixel_rgb_t fgcolor, const unsigned char* const fade_table)
{
const unsigned char * const s_fade_table = fade_table + 0x100;
bgcolor.u.element.red = fade_table[bgcolor.u.element.red] + s_fade_table[fgcolor.u.element.red];
bgcolor.u.element.green = fade_table[bgcolor.u.element.green] + s_fade_table[fgcolor.u.element.green];
bgcolor.u.element.blue = fade_table[bgcolor.u.element.blue] + s_fade_table[fgcolor.u.element.blue];
return bgcolor;
}
nana::pixel_rgb_t fade_color_intermedia(nana::pixel_rgb_t fgcolor, const unsigned char* fade_table)
{
fade_table += 0x100;
fgcolor.u.element.red = fade_table[fgcolor.u.element.red];
fgcolor.u.element.green = fade_table[fgcolor.u.element.green];
fgcolor.u.element.blue = fade_table[fgcolor.u.element.blue];
return fgcolor;
}
nana::pixel_rgb_t fade_color_by_intermedia(nana::pixel_rgb_t bgcolor, nana::pixel_rgb_t fgcolor_intermedia, const unsigned char* const fade_table)
{
bgcolor.u.element.red = fade_table[bgcolor.u.element.red] + fgcolor_intermedia.u.element.red;
bgcolor.u.element.green = fade_table[bgcolor.u.element.green] + fgcolor_intermedia.u.element.green;
bgcolor.u.element.blue = fade_table[bgcolor.u.element.blue] + fgcolor_intermedia.u.element.blue;
return bgcolor;
}
void blend(drawable_type dw, const nana::rectangle& area, unsigned color, double fade_rate)
{
if(fade_rate <= 0) return;
if(fade_rate > 1) fade_rate = 1;
nana::rectangle r;
if(false == nana::overlap(drawable_size(dw), area, r))
return;
unsigned red = static_cast<unsigned>((color & 0xFF0000) * fade_rate);
unsigned green = static_cast<unsigned>((color & 0xFF00) * fade_rate);
unsigned blue = static_cast<unsigned>((color & 0xFF) * fade_rate);
double lrate = 1 - fade_rate;
pixel_buffer pixbuf(dw, r.y, r.height);
for(std::size_t row = 0; row < r.height; ++row)
{
nana::pixel_rgb_t * i = pixbuf.raw_ptr(row) + r.x;
const nana::pixel_rgb_t * const end = i + r.width;
for(; i < end; ++i)
{
unsigned px_r = ((static_cast<unsigned>((i->u.color & 0xFF0000) * lrate) + red) & 0xFF0000);
unsigned px_g = ((static_cast<unsigned>((i->u.color & 0xFF00) * lrate) + green) & 0xFF00);
unsigned px_b = ((static_cast<unsigned>((i->u.color & 0xFF) * lrate) + blue) & 0xFF);
i->u.color = (px_r | px_g | px_b);
}
}
pixbuf.paste(nana::rectangle(r.x, 0, r.width, r.height), dw, r.x, r.y);
}
nana::size raw_text_extent_size(drawable_type dw, const nana::char_t* text, std::size_t len)
{
if(nullptr == dw || nullptr == text || 0 == len) return nana::size();
#if defined(NANA_WINDOWS)
::SIZE size;
if(::GetTextExtentPoint32(dw->context, text, static_cast<int>(len), &size))
return nana::size(size.cx, size.cy);
#elif defined(NANA_X11)
#if defined(NANA_UNICODE)
std::string utf8str = nana::charset(nana::string(text, len));
XGlyphInfo ext;
XftFont * fs = reinterpret_cast<XftFont*>(dw->font->handle);
::XftTextExtentsUtf8(nana::detail::platform_spec::instance().open_display(), fs,
reinterpret_cast<XftChar8*>(const_cast<char*>(utf8str.c_str())), utf8str.size(), &ext);
return nana::size(ext.xOff, fs->ascent + fs->descent);
#else
XRectangle ink;
XRectangle logic;
::XmbTextExtents(reinterpret_cast<XFontSet>(dw->font->handle), text, len, &ink, &logic);
return nana::size(logic.width, logic.height);
#endif
#endif
return nana::size();
}
nana::size text_extent_size(drawable_type dw, const nana::char_t * text, std::size_t len)
{
nana::size extents = raw_text_extent_size(dw, text, len);
const nana::char_t* const end = text + len;
int tabs = 0;
for(; text != end; ++text)
{
if(*text == '\t')
++tabs;
}
if(tabs)
extents.width = static_cast<int>(extents.width) - tabs * static_cast<int>(dw->string.tab_pixels - dw->string.whitespace_pixels * dw->string.tab_length);
return extents;
}
void draw_string(drawable_type dw, int x, int y, const nana::char_t * str, std::size_t len)
{
#if defined(NANA_WINDOWS)
::TextOut(dw->context, x, y, str, static_cast<int>(len));
#elif defined(NANA_X11)
#if defined(NANA_UNICODE)
std::string utf8str = nana::charset(nana::string(str, len));
XftFont * fs = reinterpret_cast<XftFont*>(dw->font->handle);
::XftDrawStringUtf8(dw->xftdraw, &(dw->xft_fgcolor), fs, x, y + fs->ascent,
reinterpret_cast<XftChar8*>(const_cast<char*>(utf8str.c_str())), utf8str.size());
#else
XFontSet fs = reinterpret_cast<XFontSet>(dw->font->handle);
XFontSetExtents * ext = ::XExtentsOfFontSet(fs);
XFontStruct ** fontstructs;
char ** font_names;
int size = ::XFontsOfFontSet(fs, &fontstructs, &font_names);
unsigned ascent = 0;
unsigned descent = 0;
XFontStruct **fontstructs_end = fontstructs + size;
for(XFontStruct** i = fontstructs; i < fontstructs_end; ++i)
{
if(ascent < (*i)->ascent)
ascent = (*i)->ascent;
if(descent < (*i)->descent)
descent = (*i)->descent;
}
XmbDrawString(display, dw->pixmap, reinterpret_cast<XFontSet>(dw->font->handle), dw->context, x, y + ascent + descent, buf, len);
#endif
#endif
}
}//end namespace detail
}//end namespace paint
}//end namespace nana

316
source/paint/gadget.cpp Normal file
View File

@@ -0,0 +1,316 @@
/*
* Graphics Gadget Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/paint/gadget.cpp
*/
#include <nana/paint/graphics.hpp>
#include <nana/paint/gadget.hpp>
namespace nana
{
namespace paint
{
namespace gadget
{
namespace detail
{
typedef nana::paint::graphics& graph_reference;
void hollow_triangle(graph_reference graph, int x, int y, nana::color_t color, uint32_t direction)
{
x += 3;
y += 3;
switch(direction)
{
case directions::to_east:
graph.line(x + 3, y + 1, x + 3, y + 9, color);
graph.line(x + 4, y + 2 , x + 7, y + 5, color);
graph.line(x + 6, y + 6, x + 4, y + 8, color);
break;
case directions::to_southeast:
graph.line(x + 2, y + 7, x + 7, y + 7, color);
graph.line(x + 7, y + 2, x + 7, y + 6, color);
graph.line(x + 3, y + 6, x + 6, y + 3, color);
break;
case directions::to_south:
y += 3;
graph.line(x, y, x + 8, y, color);
graph.line(x + 1, y + 1, x + 4, y + 4, color);
graph.line(x + 7, y + 1, x + 5, y + 3, color);
break;
case directions::to_west:
x += 5;
y += 1;
graph.line(x, y, x, y + 8, color);
graph.line(x - 4, y + 4, x - 1, y + 1, color);
graph.line(x - 3, y + 5, x - 1, y + 7, color);
break;
case directions::to_north:
y += 7;
graph.line(x, y, x + 8, y, color);
graph.line(x + 1, y - 1, x + 4, y - 4, color);
graph.line(x + 5, y - 3, x + 7, y - 1, color);
break;
}
}
void solid_triangle(graph_reference graph, int x, int y, nana::color_t color, uint32_t dir)
{
x += 3;
y += 3;
switch(dir)
{
case directions::to_east:
for(int i = 0; i < 5; ++i)
graph.line(x + 3 + i, y + 1 + i, x + 3 + i, y + 9 - i, color);
break;
case directions::to_southeast:
for(int i = 0; i < 6; ++i)
graph.line(x + 2 + i, y + 7 - i, x + 7, y + 7 - i, color);
break;
case directions::to_south:
y += 3;
for(int i = 0; i < 5; ++i)
graph.line(x + i, y + i, x + 8 - i, y + i, color);
break;
case directions::to_west:
x += 5;
y += 1;
for(int i = 0; i < 5; ++i)
graph.line(x - i, y + i, x - i, y + 8 - i, color);
break;
case directions::to_north:
y += 7;
for(int i = 0; i < 5; ++i)
graph.line(x + i, y - i, x + 8 - i, y - i, color);
break;
}
}
void direction_arrow(graph_reference graph, int x, int y, nana::color_t color, uint32_t dir)
{
y += 5;
switch(dir)
{
case directions::to_north:
{
x += 3;
int pixels = 1;
for(int l = 0; l < 4; ++l)
{
for(int i = 0; i < pixels; ++i)
{
if(l ==3 && i == 3)
{}
else
graph.set_pixel(x + i, y, 0x262);
}
x--;
y++;
pixels += 2;
}
graph.set_pixel(x + 1, y, 0x262);
graph.set_pixel(x + 2, y, 0x262);
graph.set_pixel(x + 6, y, 0x262);
graph.set_pixel(x + 7, y, 0x262);
}
break;
case directions::to_south:
{
graph.set_pixel(x, y, 0x262);
graph.set_pixel(x + 1, y, 0x262);
graph.set_pixel(x + 5, y, 0x262);
graph.set_pixel(x + 6, y, 0x262);
++y;
int pixels = 7;
for(int l = 0; l < 4; ++l)
{
for(int i = 0; i < pixels; ++i)
{
if(l == 0 && i == 3){}
else
graph.set_pixel(x + i, y, 0x262);
}
x++;
y++;
pixels -= 2;
}
}
break;
}
}
void double_arrow_line(nana::paint::graphics & graph, int x, int y, color_t color, bool horizontal)
{
graph.set_pixel(x, y, color);
if(horizontal)
{
graph.set_pixel(x + 1, y, color);
graph.set_pixel(x + 4, y, color);
graph.set_pixel(x + 5, y, color);
}
else
{
graph.set_pixel(x, y + 1, color);
graph.set_pixel(x, y + 4, color);
graph.set_pixel(x, y + 5, color);
}
}
void double_arrow(nana::paint::graphics& graph, int x, int y, color_t color, directions::t dir)
{
switch(dir)
{
case directions::to_east:
double_arrow_line(graph, x + 4, y + 6, color, true);
double_arrow_line(graph, x + 5, y + 7, color, true);
double_arrow_line(graph, x + 6, y + 8, color, true);
double_arrow_line(graph, x + 5, y + 9, color, true);
double_arrow_line(graph, x + 4, y + 10, color, true);
break;
case directions::to_west:
double_arrow_line(graph, x + 5, y + 6, color, true);
double_arrow_line(graph, x + 4, y + 7, color, true);
double_arrow_line(graph, x + 3, y + 8, color, true);
double_arrow_line(graph, x + 4, y + 9, color, true);
double_arrow_line(graph, x + 5, y + 10, color, true);
break;
case directions::to_south:
double_arrow_line(graph, x + 5, y + 4, color, false);
double_arrow_line(graph, x + 6, y + 5, color, false);
double_arrow_line(graph, x + 7, y + 6, color, false);
double_arrow_line(graph, x + 8, y + 5, color, false);
double_arrow_line(graph, x + 9, y + 4, color, false);
break;
case directions::to_north:
double_arrow_line(graph, x + 5, y + 6, color, false);
double_arrow_line(graph, x + 6, y + 5, color, false);
double_arrow_line(graph, x + 7, y + 4, color, false);
double_arrow_line(graph, x + 8, y + 5, color, false);
double_arrow_line(graph, x + 9, y + 6, color, false);
break;
default:
break;
}
}
}//end namespace detail
//arrow_16_pixels
//param@style: 0 = hollow, 1 = solid
void arrow_16_pixels(nana::paint::graphics& graph, int x, int y, unsigned color, uint32_t style, directions::t dir)
{
switch(style)
{
case 1:
detail::solid_triangle(graph, x, y, color, dir);
break;
case 2:
detail::direction_arrow(graph, x, y, color, dir);
break;
case 3:
detail::double_arrow(graph, x, y, color, dir);
break;
case 0:
default:
detail::hollow_triangle(graph, x, y, color, dir);
break;
}
}
void close_16_pixels(nana::paint::graphics& graph, int x, int y, uint32_t style, uint32_t color)
{
if(0 == style)
{
x += 3;
y += 3;
graph.line(x, y, x + 9, y + 9, color);
graph.line(x + 1, y, x + 9, y + 8, color);
graph.line(x, y + 1, x + 8, y + 9, color);
graph.line(x + 9, y, x , y + 9, color);
graph.line(x + 8, y, x, y + 8, color);
graph.line(x + 9, y + 1, x + 1, y + 9, color);
}
else
{
x += 4;
y += 4;
graph.line(x, y, x + 7, y + 7, color);
graph.line(x + 1, y, x + 7, y + 6, color);
graph.line(x, y + 1, x + 6, y + 7, color);
graph.line(x + 7, y, x , y + 7, color);
graph.line(x + 6, y, x, y + 6, color);
graph.line(x + 7, y + 1, x + 1, y + 7, color);
}
}
void cross(graphics& graph, int x, int y, uint32_t size, uint32_t thickness, nana::color_t color)
{
if(thickness + 2 <= size)
{
int gap = (size - thickness) / 2;
nana::point ps[12];
ps[0].x = x + gap;
ps[1].x = ps[0].x + thickness - 1;
ps[1].y = ps[0].y = y;
ps[2].x = ps[1].x;
ps[2].y = y + gap;
ps[3].x = ps[2].x + gap;
ps[3].y = ps[2].y;
ps[4].x = ps[3].x;
ps[4].y = ps[3].y + thickness - 1;
ps[5].x = ps[1].x;
ps[5].y = ps[4].y;
ps[6].x = ps[5].x;
ps[6].y = ps[5].y + gap;
ps[7].x = x + gap;
ps[7].y = ps[6].y;
ps[8].x = ps[7].x;
ps[8].y = ps[4].y;
ps[9].x = x;
ps[9].y = ps[4].y;
ps[10].x = x;
ps[10].y = y + gap;
ps[11].x = x + gap;
ps[11].y = y + gap;
nana::color_t dkcolor = graph.mix(color, 0x0, 0.5);
for(int i = 0; i < 11; ++i)
graph.line(ps[i], ps[i + 1], dkcolor);
graph.line(ps[11], ps[0], dkcolor);
graph.rectangle(ps[10].x + 1, ps[10].y + 1, (gap << 1) + thickness - 2, thickness - 2, color, true);
graph.rectangle(ps[0].x + 1, ps[0].y + 1, thickness - 2, (gap << 1) + thickness - 2, color, true);
}
}
}//end namespace gadget
}//end namespace paint
}//end namespace nana

1189
source/paint/graphics.cpp Normal file

File diff suppressed because it is too large Load Diff

276
source/paint/image.cpp Normal file
View File

@@ -0,0 +1,276 @@
/*
* Paint Image Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/paint/image.cpp
*/
#include <nana/config.hpp>
#include PLATFORM_SPEC_HPP
#include <nana/paint/image.hpp>
#include <algorithm>
#include <fstream>
#include <iterator>
#include <nana/paint/detail/image_impl_interface.hpp>
#include <nana/paint/pixel_buffer.hpp>
#if defined(NANA_ENABLE_PNG)
#include <nana/paint/detail/image_png.hpp>
#endif
#include <nana/paint/detail/image_bmp.hpp>
#include <nana/paint/detail/image_ico.hpp>
namespace nana
{
namespace paint
{
namespace detail
{
//class image_ico
image_ico::image_ico(bool is_ico): is_ico_(is_ico){}
bool image_ico::open(const nana::char_t* filename)
{
close();
#if defined(NANA_WINDOWS)
HICON handle = 0;
if(is_ico_)
{
handle = (HICON)::LoadImage(0, filename, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
}
else
{
SHFILEINFO sfi;
::SHGetFileInfo(filename, 0, &sfi, sizeof(sfi), SHGFI_ICON);
handle = sfi.hIcon;
}
if(handle)
{
HICON * p = new HICON(handle);
ptr_ = std::shared_ptr<HICON>(p, handle_deleter());
ICONINFO info;
::GetIconInfo(handle, &info);
size_.width = (info.xHotspot << 1);
size_.height = (info.yHotspot << 1);
::DeleteObject(info.hbmColor);
::DeleteObject(info.hbmMask);
return true;
}
#endif
return false;
}
bool image_ico::alpha_channel() const
{
return false;
}
bool image_ico::empty() const
{
return (nullptr == ptr_);
}
void image_ico::close()
{
ptr_.reset();
}
nana::size image_ico::size() const
{
return size_;
}
void image_ico::paste(const nana::rectangle& src_r, graph_reference graph, int x, int y) const
{
if(ptr_ && (graph.empty() == false))
{
#if defined(NANA_WINDOWS)
::DrawIconEx(graph.handle()->context, x, y, *ptr_, src_r.width, src_r.height, 0, 0, DI_NORMAL);
#endif
}
}
void image_ico::stretch(const nana::rectangle&, graph_reference graph, const nana::rectangle& r) const
{
if(ptr_ && (graph.empty() == false))
{
#if defined(NANA_WINDOWS)
::DrawIconEx(graph.handle()->context, r.x, r.y, *ptr_, r.width, r.height, 0, 0, DI_NORMAL);
#endif
}
}
const image_ico::ptr_t& image_ico::ptr() const
{
return ptr_;
}
#if defined(NANA_WINDOWS)
//struct handle_deleter
void image_ico::handle_deleter::operator()(HICON* p) const
{
if(p && *p)
::DestroyIcon(*p);
delete p;
}
//end struct handle_deleter
#endif
//end class image_ico
}
image::image_impl_interface::~image_impl_interface()
{}
namespace detail
{
int toupper(int c)
{
return (('a' <= c && c <= 'z') ?
c - ('a' - 'A')
: c);
}
}//end namespace detail
//class image
image::image()
{}
image::image(const image& rhs)
: image_ptr_(rhs.image_ptr_)
{}
image::image(image&& r)
: image_ptr_(std::move(r.image_ptr_))
{}
image::image(const nana::char_t* file)
{
if(file)
open(file);
}
image::image(const nana::string& file)
{
this->open(file);
}
image::~image()
{
close();
}
image& image::operator=(const image& r)
{
if(this != &r)
image_ptr_ = r.image_ptr_;
return * this;
}
image& image::operator=(image&& r)
{
if(this != &r)
image_ptr_ = std::move(r.image_ptr_);
return *this;
}
bool image::open(const nana::string& filename)
{
image_ptr_.reset();
image::image_impl_interface * helper = nullptr;
if(filename.size())
{
nana::string fn;
std::transform(filename.cbegin(), filename.cend(), std::back_inserter(fn), detail::toupper);
if(filename.size() >= 4)
{
nana::string suffix = fn.substr(fn.size() - 4);
if(STR(".ICO") == suffix)
{
#if defined(NANA_WINDOWS)
helper = new detail::image_ico(true);
#endif
}
#if defined(NANA_ENABLE_PNG)
else if(STR(".PNG") == suffix)
helper = new detail::image_png;
#endif
}
if(0 == helper)
{
#if defined(NANA_UNICODE)
std::ifstream ifs(std::string(nana::charset(filename)).c_str(), std::ios::binary);
#else
std::ifstream ifs(filename.c_str(), std::ios::binary);
#endif
if(ifs)
{
unsigned short meta = 0;
ifs.read(reinterpret_cast<char*>(&meta), 2);
if(*reinterpret_cast<const short*>("BM") == meta)
helper = new detail::image_bmp;
else if(*reinterpret_cast<const short*>("MZ") == meta)
helper = new detail::image_ico(false);
}
}
if(helper)
{
image_ptr_ = std::shared_ptr<image_impl_interface>(helper);
return helper->open(filename.data());
}
}
return false;
}
bool image::empty() const
{
return ((nullptr == image_ptr_) || image_ptr_->empty());
}
image::operator unspecified_bool_t() const
{
return (image_ptr_ ? &image::empty : nullptr);
}
void image::close()
{
image_ptr_.reset();
}
nana::size image::size() const
{
return (image_ptr_ ? image_ptr_->size() : nana::size());
}
void image::paste(graphics& dst, int x, int y) const
{
if(image_ptr_)
image_ptr_->paste(image_ptr_->size(), dst, x, y);
}
void image::paste(const nana::rectangle& r_src, graphics & dst, const nana::point& p_dst) const
{
if(image_ptr_)
image_ptr_->paste(r_src, dst, p_dst.x, p_dst.y);
}
void image::stretch(const nana::rectangle& r_src, graphics& dst, const nana::rectangle & r_dst) const
{
if(image_ptr_)
image_ptr_->stretch(r_src, dst, r_dst);
}
//end class image
}//end namespace paint
}//end namespace nana

View File

@@ -0,0 +1,42 @@
#include <nana/paint/image_process_selector.hpp>
namespace nana
{
namespace paint
{
namespace image_process
{
//class selector
void selector::stretch(const std::string& name)
{
detail::image_process_provider & p = detail::image_process_provider::instance();
p.set(p.ref_stretch_tag(), name);
}
void selector::alpha_blend(const std::string& name)
{
detail::image_process_provider & p = detail::image_process_provider::instance();
p.set(p.ref_alpha_blend_tag(), name);
}
void selector::blend(const std::string& name)
{
detail::image_process_provider & p = detail::image_process_provider::instance();
p.set(p.ref_blend_tag(), name);
}
void selector::line(const std::string& name)
{
detail::image_process_provider & p = detail::image_process_provider::instance();
p.set(p.ref_line_tag(), name);
}
void selector::blur(const std::string& name)
{
detail::image_process_provider & p = detail::image_process_provider::instance();
p.set(p.ref_blur_tag(), name);
}
//end class selector
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,590 @@
#include <nana/config.hpp>
#include PLATFORM_SPEC_HPP
#include <nana/paint/text_renderer.hpp>
#include <nana/unicode_bidi.hpp>
#include <nana/paint/detail/native_paint_interface.hpp>
namespace nana
{
namespace paint
{
namespace helper
{
template<typename F>
void for_each_line(const nana::char_t * str, std::size_t len, int top, F & f)
{
auto head = str;
auto end = str + len;
for(auto i = str; i != end; ++i)
{
if(*i == '\n')
{
top += static_cast<int>(f(top, head, i - head));
head = i + 1;
}
}
if(head != end)
f(top, head, end - head);
}
struct draw_string
{
drawable_type dw;
const int x, endpos;
nana::unicode_bidi bidi;
std::vector<nana::unicode_bidi::entity> reordered;
align text_align;
draw_string(drawable_type dw, int x, int endpos, align ta)
: dw(dw), x(x), endpos(endpos), text_align(ta)
{}
unsigned operator()(int top, const nana::char_t * buf, std::size_t bufsize)
{
int xpos = x;
unsigned pixels = 0;
bidi.linestr(buf, bufsize, reordered);
switch(text_align)
{
case align::left:
for(auto & ent : reordered)
{
std::size_t len = ent.end - ent.begin;
nana::size ts = detail::text_extent_size(dw, ent.begin, len);
if(ts.height > pixels) pixels = ts.height;
if(xpos + static_cast<int>(ts.width) > 0)
detail::draw_string(dw, xpos, top, ent.begin, len);
xpos += static_cast<int>(ts.width);
if(xpos >= endpos)
break;
}
break;
case align::center:
{
unsigned lenpx = 0;
std::vector<unsigned> widths;
for(auto & ent : reordered)
{
auto ts = detail::text_extent_size(dw, ent.begin, ent.end - ent.begin);
if(ts.height > pixels) pixels = ts.height;
lenpx += ts.width;
widths.push_back(ts.width);
}
xpos += (endpos - xpos - static_cast<int>(lenpx))/2;
auto ipx = widths.begin();
for(auto & ent : reordered)
{
if(xpos + static_cast<int>(*ipx) > 0)
detail::draw_string(dw, xpos, top, ent.begin, ent.end - ent.begin);
xpos += static_cast<int>(*ipx);
if(xpos >= endpos)
break;
}
}
break;
case align::right:
{
int xend = endpos;
std::swap(xpos, xend);
for(auto i = reordered.rbegin(), end = reordered.rend(); i != end; ++i)
{
auto & ent = *i;
std::size_t len = ent.end - ent.begin;
nana::size ts = detail::text_extent_size(dw, ent.begin, len);
if(ts.height > pixels) pixels = ts.height;
if(xpos > xend)
{
xpos -= static_cast<int>(ts.width);
detail::draw_string(dw, xpos, top, i->begin, len);
}
if(xpos <= xend || xpos <= 0)
break;
}
}
break;
}
return pixels;
}
};
struct draw_string_omitted
{
graphics & graph;
int x, endpos;
nana::color_t color;
unsigned omitted_pixels;
nana::unicode_bidi bidi;
std::vector<nana::unicode_bidi::entity> reordered;
draw_string_omitted(graphics& graph, int x, int endpos, nana::color_t color, bool omitted)
: graph(graph), x(x), endpos(endpos), color(color)
{
omitted_pixels = (omitted ? graph.text_extent_size(STR("..."), 3).width : 0);
if(endpos - x > static_cast<int>(omitted_pixels))
this->endpos -= omitted_pixels;
else
this->endpos = x;
}
unsigned operator()(int top, const nana::char_t * buf, std::size_t bufsize)
{
drawable_type dw = graph.handle();
int xpos = x;
unsigned pixels = 0;
bidi.linestr(buf, bufsize, reordered);
for(auto & i : reordered)
{
std::size_t len = i.end - i.begin;
nana::size ts = detail::text_extent_size(dw, i.begin, len);
if(ts.height > pixels) pixels = ts.height;
if(xpos + static_cast<int>(ts.width) <= endpos)
{
detail::draw_string(dw, xpos, top, i.begin, len);
xpos += static_cast<int>(ts.width);
}
else
{
nana::rectangle r;
r.width = endpos - xpos;
r.height = ts.height;
nana::paint::graphics dum_graph(r.width, r.height);
dum_graph.bitblt(r, graph, nana::point(xpos, top));
dum_graph.string(0, 0, color, i.begin, len);
r.x = xpos;
r.y = top;
graph.bitblt(r, dum_graph);
if(omitted_pixels)
detail::draw_string(dw, endpos, top, STR("..."), 3);
break;
}
}
return pixels;
}
};
struct draw_string_auto_changing_lines
{
graphics & graph;
int x, endpos;
nana::unicode_bidi bidi;
std::vector<nana::unicode_bidi::entity> reordered;
std::vector<nana::size> ts_keeper;
align text_align;
draw_string_auto_changing_lines(graphics& graph, int x, int endpos, align ta)
: graph(graph), x(x), endpos(endpos), text_align(ta)
{}
unsigned operator()(int top, const nana::char_t * buf, std::size_t bufsize)
{
unsigned pixels = 0;
drawable_type dw = graph.handle();
unsigned str_w = 0;
ts_keeper.clear();
bidi.linestr(buf, bufsize, reordered);
for(auto & i : reordered)
{
nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin);
if(ts.height > pixels) pixels = ts.height;
ts_keeper.push_back(ts);
str_w += ts.width;
}
//Test whether the text needs the new line.
if(x + static_cast<int>(str_w) > endpos)
{
pixels = 0;
unsigned line_pixels = 0;
int xpos = x;
int orig_top = top;
auto i_ts_keeper = ts_keeper.cbegin();
for(auto & i : reordered)
{
if(line_pixels < i_ts_keeper->height)
line_pixels = i_ts_keeper->height;
bool beyond_edge = (xpos + static_cast<int>(i_ts_keeper->width) > endpos);
if(beyond_edge)
{
std::size_t len = i.end - i.begin;
if(len > 1)
{
unsigned * pxbuf = new unsigned[len];
//Find the char that should be splitted
graph.glyph_pixels(i.begin, len, pxbuf);
std::size_t idx_head = 0, idx_splitted;
do
{
idx_splitted = find_splitted(idx_head, len, xpos, endpos, pxbuf);
if(idx_splitted == len)
{
detail::draw_string(dw, xpos, top, i.begin + idx_head, idx_splitted - idx_head);
for(std::size_t i = idx_head; i < len; ++i)
xpos += static_cast<int>(pxbuf[i]);
break;
}
//Check the word whether it is splittable.
if(splittable(i.begin, idx_splitted))
{
detail::draw_string(dw, xpos, top, i.begin + idx_head, idx_splitted - idx_head);
idx_head = idx_splitted;
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
else
{
//Search the splittable character from idx_head to idx_splitted
const nana::char_t * u = i.begin + idx_splitted;
const nana::char_t * head = i.begin + idx_head;
for(; head < u; --u)
{
if(splittable(head, u - head))
break;
}
if(u != head)
{
detail::draw_string(dw, xpos, top, head, u - head);
idx_head += u - head;
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
else
{
u = i.begin + idx_splitted;
const nana::char_t * end = i.begin + len;
for(; u < end; ++u)
{
if(splittable(head, u - head))
break;
}
std::size_t splen = u - head;
top += line_pixels;
xpos = x;
detail::draw_string(dw, x, top, head, splen);
line_pixels = i_ts_keeper->height;
for(std::size_t k = idx_head; k < idx_head + splen; ++k)
xpos += static_cast<int>(pxbuf[k]);
if(xpos >= endpos)
{
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
idx_head += splen;
}
}
}while(idx_head < len);
delete [] pxbuf;
}
else
{
detail::draw_string(dw, x, top += static_cast<int>(line_pixels), i.begin, 1);
xpos = x + static_cast<int>(i_ts_keeper->width);
}
line_pixels = 0;
}
else
{
detail::draw_string(dw, xpos, top, i.begin, i.end - i.begin);
xpos += static_cast<int>(i_ts_keeper->width);
}
++i_ts_keeper;
}
pixels = (top - orig_top) + line_pixels;
}
else
{
//The text could be drawn in a line.
if((align::left == text_align) || (align::center == text_align))
{
int xpos = x;
if(align::center == text_align)
xpos += (endpos - x - static_cast<int>(str_w)) / 2;
auto i_ts_keeper = ts_keeper.cbegin();
for(auto & ent : reordered)
{
const nana::size & ts = *i_ts_keeper;
if(xpos + static_cast<int>(ts.width) > 0)
detail::draw_string(dw, xpos, top, ent.begin, ent.end - ent.begin);
xpos += static_cast<int>(ts.width);
++i_ts_keeper;
}
}
else if(align::right == text_align)
{
int xpos = endpos;
auto i_ts_keeper = ts_keeper.crbegin();
for(auto i = reordered.crbegin(), end = reordered.crend(); i != end; ++i)
{
auto & ent = *i;
std::size_t len = ent.end - ent.begin;
const nana::size & ts = *i_ts_keeper;
xpos -= ts.width;
if(xpos >= 0)
detail::draw_string(dw, xpos, top, ent.begin, len);
++i_ts_keeper;
}
}
}
return pixels;
}
static std::size_t find_splitted(std::size_t begin, std::size_t end, int x, int endpos, unsigned * pxbuf)
{
unsigned acc_width = 0;
for(std::size_t i = begin; i < end; ++i)
{
if(x + static_cast<int>(acc_width + pxbuf[i]) > endpos)
{
if(i == begin)
++i;
return i;
}
acc_width += pxbuf[i];
}
return end;
}
static bool splittable(const nana::char_t * str, std::size_t index)
{
nana::char_t ch = str[index];
if(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'))
{
nana::char_t prch;
if(index)
{
prch = str[index - 1];
if('0' <= ch && ch <= '9')
return !(('0' <= prch && prch <= '9') || (str[index - 1] == '-'));
return (('z' < prch || prch < 'a') && ('Z' < prch || prch < 'A'));
}
else
return false;
}
return true;
}
};
struct extent_auto_changing_lines
{
graphics & graph;
int x, endpos;
nana::unicode_bidi bidi;
std::vector<nana::unicode_bidi::entity> reordered;
std::vector<nana::size> ts_keeper;
unsigned extents;
extent_auto_changing_lines(graphics& graph, int x, int endpos)
: graph(graph), x(x), endpos(endpos), extents(0)
{}
unsigned operator()(int top, const nana::char_t * buf, std::size_t bufsize)
{
unsigned pixels = 0;
drawable_type dw = graph.handle();
unsigned str_w = 0;
ts_keeper.clear();
bidi.linestr(buf, bufsize, reordered);
for(auto & i : reordered)
{
nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin);
ts_keeper.push_back(ts);
str_w += ts.width;
}
auto i_ts_keeper = ts_keeper.cbegin();
//Test whether the text needs the new line.
if(x + static_cast<int>(str_w) > endpos)
{
unsigned line_pixels = 0;
int xpos = x;
int orig_top = top;
for(auto & i : reordered)
{
if(line_pixels < i_ts_keeper->height)
line_pixels = i_ts_keeper->height;
bool beyond_edge = (xpos + static_cast<int>(i_ts_keeper->width) > endpos);
if(beyond_edge)
{
std::size_t len = i.end - i.begin;
if(len > 1)
{
unsigned * pxbuf = new unsigned[len];
//Find the char that should be splitted
graph.glyph_pixels(i.begin, len, pxbuf);
std::size_t idx_head = 0, idx_splitted;
do
{
idx_splitted = draw_string_auto_changing_lines::find_splitted(idx_head, len, xpos, endpos, pxbuf);
if(idx_splitted == len)
{
for(std::size_t i = idx_head; i < len; ++i)
xpos += static_cast<int>(pxbuf[i]);
break;
}
//Check the word whether it is splittable.
if(draw_string_auto_changing_lines::splittable(i.begin, idx_splitted))
{
idx_head = idx_splitted;
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
else
{
//Search the splittable character from idx_head to idx_splitted
const nana::char_t * u = i.begin + idx_splitted;
const nana::char_t * head = i.begin + idx_head;
for(; head < u; --u)
{
if(draw_string_auto_changing_lines::splittable(head, u - head))
break;
}
if(u != head)
{
idx_head += u - head;
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
else
{
u = i.begin + idx_splitted;
const nana::char_t * end = i.begin + len;
for(; u < end; ++u)
{
if(draw_string_auto_changing_lines::splittable(head, u - head))
break;
}
std::size_t splen = u - head;
top += line_pixels;
xpos = x;
line_pixels = i_ts_keeper->height;
for(std::size_t k = idx_head; k < idx_head + splen; ++k)
xpos += static_cast<int>(pxbuf[k]);
if(xpos >= endpos)
{
xpos = x;
top += line_pixels;
line_pixels = i_ts_keeper->height;
}
idx_head += splen;
}
}
}while(idx_head < len);
delete [] pxbuf;
}
else
xpos = x + static_cast<int>(i_ts_keeper->width);
line_pixels = 0;
}
else
xpos += static_cast<int>(i_ts_keeper->width);
++i_ts_keeper;
}
pixels = (top - orig_top) + line_pixels;
}
else
{
while(i_ts_keeper != ts_keeper.cend())
{
const nana::size & ts = *(i_ts_keeper++);
if(ts.height > pixels) pixels = ts.height;
}
}
extents += pixels;
return pixels;
}
};
}//end namespace helper
//class text_renderer
text_renderer::text_renderer(graph_reference graph, align ta)
: graph_(graph), text_align_(ta)
{}
void text_renderer::render(int x, int y, nana::color_t col, const nana::char_t * str, std::size_t len)
{
if(graph_)
{
helper::draw_string ds(graph_.handle(), x, static_cast<int>(graph_.width()), text_align_);
ds.dw->fgcolor(col);
helper::for_each_line(str, len, y, ds);
}
}
void text_renderer::render(int x, int y, nana::color_t col, const nana::char_t * str, std::size_t len, unsigned restricted_pixels, bool omitted)
{
if(graph_)
{
helper::draw_string_omitted dso(graph_, x, x + static_cast<int>(restricted_pixels), col, omitted);
graph_.handle()->fgcolor(col);
helper::for_each_line(str, len, y, dso);
}
}
void text_renderer::render(int x, int y, nana::color_t col, const nana::char_t * str, std::size_t len, unsigned restricted_pixels)
{
if(graph_)
{
helper::draw_string_auto_changing_lines dsacl(graph_, x, x + static_cast<int>(restricted_pixels), text_align_);
graph_.handle()->fgcolor(col);
helper::for_each_line(str, len, y, dsacl);
}
}
nana::size text_renderer::extent_size(int x, int y, const nana::char_t* str, std::size_t len, unsigned restricted_pixels) const
{
nana::size extents;
if(graph_)
{
helper::extent_auto_changing_lines eacl(graph_, x, x + static_cast<int>(restricted_pixels));
helper::for_each_line(str, len, y, eacl);
extents.width = restricted_pixels;
extents.height = eacl.extents;
}
return extents;
}
//end class text_renderer
}
}

173
source/system/dataexch.cpp Normal file
View File

@@ -0,0 +1,173 @@
/*
* Data Exchanger Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/system/dataexch.cpp
* @description: An implementation of a data exchange mechanism through Windows Clipboard, X11 Selection.
*/
#include <nana/system/dataexch.hpp>
#include <nana/traits.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_X11)
#include PLATFORM_SPEC_HPP
#include GUI_BEDROCK_HPP
#include <nana/gui/detail/basic_window.hpp>
#endif
namespace nana{ namespace system{
//class dataexch
void dataexch::set(const nana::char_t* text)
{
_m_set(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, text, (nana::strlen(text) + 1) * sizeof(nana::char_t));
}
void dataexch::set(const nana::string& text)
{
_m_set(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, text.c_str(), (text.length() + 1) * sizeof(nana::char_t));
}
void dataexch::get(nana::string& str)
{
std::size_t size;
void* res = _m_get(std::is_same<char, nana::char_t>::value ? format::text : format::unicode, size);
if(res)
{
#if defined(NANA_X11) && defined(NANA_UNICODE)
nana::detail::charset_conv conv("UTF-32", "UTF-8");
const std::string & utf32str = conv.charset(reinterpret_cast<char*>(res), size);
const nana::char_t * utf32ptr = reinterpret_cast<const nana::char_t*>(utf32str.c_str());
str.append(utf32ptr + 1, utf32ptr + utf32str.size() / sizeof(nana::char_t));
#else
str.reserve(size / sizeof(nana::char_t));
str.append(reinterpret_cast<nana::char_t*>(res), reinterpret_cast<nana::char_t*>(res) + size / sizeof(nana::char_t));
nana::string::size_type pos = str.find_last_not_of(nana::char_t(0));
if(pos != str.npos)
str.erase(pos + 1);
#endif
#if defined(NANA_X11)
::XFree(res);
#endif
}
}
//private:
bool dataexch::_m_set(unsigned type, const void* buf, std::size_t size)
{
bool res = false;
#if defined(NANA_WINDOWS)
if(type < format::end && ::OpenClipboard(::GetFocus()))
{
if(::EmptyClipboard())
{
HGLOBAL g = ::GlobalAlloc(GHND | GMEM_SHARE, size);
void * addr = ::GlobalLock(g);
memcpy(addr, buf, size);
::GlobalUnlock(g);
switch(type)
{
case format::text: type = CF_TEXT; break;
case format::unicode: type = CF_UNICODETEXT; break;
case format::pixmap: type = CF_BITMAP; break;
}
::SetClipboardData(type, g);
res = true;
}
::CloseClipboard();
}
#elif defined(NANA_X11)
auto & spec = ::nana::detail::platform_spec::instance();
native_window_type owner = nullptr;
{
internal_scope_guard lock;
auto wd = detail::bedrock::instance().focus();
if(wd) owner = wd->root;
}
if(owner)
{
Atom atom_type;
switch(type)
{
case format::text: atom_type = XA_STRING; break;
case format::unicode: atom_type = spec.atombase().utf8_string; break;
default:
return false;
}
#if defined(NANA_UNICODE)
//The internal clipboard stores UTF8_STRING, the parameter string should be converted from utf32 to utf8.
nana::detail::charset_conv conv("UTF-8", "UTF-32");
std::string utf8str = conv.charset(reinterpret_cast<const char*>(buf), size);
buf = utf8str.c_str();
size = utf8str.size();
#endif
spec.write_selection(owner, atom_type, buf, size);
return true;
}
#endif
return res;
}
void* dataexch::_m_get(unsigned type, size_t& size)
{
void* res = 0;
#if defined(NANA_WINDOWS)
if(type < format::end && ::OpenClipboard(::GetFocus()))
{
switch(type)
{
case format::text: type = CF_TEXT; break;
case format::unicode: type = CF_UNICODETEXT; break;
case format::pixmap: type = CF_BITMAP; break;
}
HANDLE handle = ::GetClipboardData(type);
if(handle)
{
res = reinterpret_cast<HGLOBAL>(::GlobalLock(handle));
if(res)
size = ::GlobalSize(handle);
}
::CloseClipboard();
}
#elif defined(NANA_X11)
nana::detail::platform_spec & spec = nana::detail::platform_spec::instance();
native_window_type requester = nullptr;
spec.lock_xlib();
{
internal_scope_guard isg;
detail::bedrock::core_window_t * wd = detail::bedrock::instance().focus();
if(wd) requester = wd->root;
}
spec.unlock_xlib();
if(requester)
{
Atom atom;
switch(type)
{
case format::text: atom = XA_STRING; break;
case format::unicode: atom = spec.atombase().utf8_string; break;
default:
return 0;
}
res = spec.request_selection(requester, atom, size);
}
#endif
return res;
}
//end class dataexch
}//end namespace system
}//end namespace nana

116
source/system/platform.cpp Normal file
View File

@@ -0,0 +1,116 @@
/*
* A platform API implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/system/platform.cpp
* @description:
* this implements some API for platform-independent programming
*/
#include <nana/deploy.hpp>
#if defined(NANA_WINDOWS)
#include <windows.h>
#include PLATFORM_SPEC_HPP
#elif defined(NANA_LINUX)
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/syscall.h>
#endif
namespace nana
{
namespace system
{
//sleep
//@brief: Suspend current thread for a specified milliseconds.
// its precision is depended on hardware.
void sleep(unsigned milliseconds)
{
#if defined(NANA_WINDOWS)
::Sleep(milliseconds);
#elif defined(NANA_LINUX)
struct timespec timeOut, remains;
timeOut.tv_sec = milliseconds / 1000;
timeOut.tv_nsec = (milliseconds % 1000) * 1000000;
while(-1 == ::nanosleep(&timeOut, &remains))
{
if(errno == EINTR)
timeOut = remains;
else
break;
}
#endif
}
//this_thread_id
//@brief: get the identifier of calling thread.
unsigned long this_thread_id()
{
#if defined(NANA_WINDOWS)
return ::GetCurrentThreadId();
#elif defined(NANA_LINUX)
return ::syscall(__NR_gettid);
#endif
}
unsigned long timestamp()
{
#if defined(NANA_WINDOWS)
return ::GetTickCount();
#elif defined(NANA_LINUX)
struct timeval tv;
::gettimeofday(&tv, 0);
return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
#endif
}
bool get_async_mouse_state(int button)
{
#if defined(NANA_WINDOWS)
bool swap = (::GetSystemMetrics(SM_SWAPBUTTON) != 0);
switch(button)
{
case 1: //Right
button = swap ? VK_LBUTTON : VK_RBUTTON;
break;
case 2:
button = VK_MBUTTON;
break;
default:
button = swap ? VK_RBUTTON : VK_LBUTTON;
break;
}
return (::GetAsyncKeyState(button) != 0);
#elif defined(NANA_LINUX)
return false;
#endif
}
//open an url through a default browser
void open_url(const nana::string& url)
{
if(url.empty())
return;
#if defined(NANA_WINDOWS)
if(::ShellExecute(0, STR("open"), url.c_str(), 0, 0, SW_SHOWNORMAL) < reinterpret_cast<HINSTANCE>(32))
{
//Because ShellExecute can delegate execution to Shell extensions (data sources, context menu handlers,
//verb implementations) that are activated using Component Object Model (COM), COM should be initialized
//before ShellExecute is called. Some Shell extensions require the COM single-threaded apartment (STA) type.
//In that case, COM should be initialized under WinXP.
nana::detail::platform_spec::co_initializer co_init;
::ShellExecute(0, STR("open"), url.c_str(), 0, 0, SW_SHOWNORMAL);
}
#elif defined(NANA_LINUX)
#endif
}
}//end namespace system
}//end namespace nana

View File

@@ -0,0 +1,125 @@
/*
* Operation System Shared Linkage Library Wrapper Implementation
* Copyright(C) 2003 Jinhao
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/system/shared_wrapper.cpp
*/
#include <nana/system/shared_wrapper.hpp>
#include <algorithm>
#include <iterator>
#ifdef NANA_LINUX
#include <dlfcn.h>
#else
#include <windows.h>
#endif
namespace nana
{
namespace system
{
namespace detail
{
namespace shared_helper
{
module_t open(const char* filename)
{
#ifdef NANA_LINUX
return ::dlopen(filename, RTLD_LAZY);
#else
return ::LoadLibraryA(filename);
#endif
}
void* symbols(module_t handle, const char* symbol)
{
#ifdef NANA_LINUX
return ::dlsym(handle, const_cast<char*>(symbol));
#else
return (void*)(::GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol));
#endif
}
void close(module_t handle)
{
#ifdef NANA_LINUX
::dlclose(handle);
#else
::FreeLibrary(reinterpret_cast<HMODULE>(handle));
#endif
}
}; //end struct shared_helper
}//end namespace detail
shared_wrapper::impl_type::impl_type()
{}
//class shared_wrapper
shared_wrapper::shared_wrapper()
{}
shared_wrapper::shared_wrapper(const char* filename)
{
this->open(filename);
}
shared_wrapper::~shared_wrapper()
{
this->close();
}
bool shared_wrapper::open(const char* filename)
{
this->close();
if(filename)
{
std::string file;
std::string ofn = filename;
std::string::size_type length = ofn.length();
if(length > 13)
{
std::transform(filename + length - 13, filename + length , std::back_inserter(file), tolower);
if(file == ".nana_shared")
{
#ifdef NANA_LINUX
ofn.replace(length - 13, 13, ".so");
#else
ofn.replace(length - 13, 13, ".DLL");
#endif
filename = ofn.c_str();
}
}
impl_.handle = detail::shared_helper::open(filename);
}
return (impl_.handle != 0);
}
void shared_wrapper::close()
{
if(impl_.handle)
{
detail::shared_helper::close(impl_.handle);
impl_.symbol = "";
impl_.proc_address = 0;
impl_.handle = 0;
}
}
bool shared_wrapper::empty() const
{
return (impl_.handle == 0);
}
//end class shared_wrapper
}//end namespace system
}//end namespace nana

View File

@@ -0,0 +1,76 @@
#include <nana/system/timepiece.hpp>
#include <nana/config.hpp>
#ifdef NANA_WINDOWS
#include <windows.h>
#elif defined(NANA_LINUX)
#include <sys/time.h>
#endif
namespace nana
{
namespace system
{
//class timepiece
struct timepiece::impl_t
{
#if defined(NANA_WINDOWS)
LARGE_INTEGER beg_timestamp;
#elif defined(NANA_LINUX)
struct timeval beg_timestamp;
#endif
};
timepiece::timepiece()
: impl_(new impl_t)
{}
timepiece::timepiece(const volatile timepiece& rhs)
: impl_(new impl_t(*rhs.impl_))
{}
timepiece::~timepiece()
{
delete impl_;
}
timepiece & timepiece::operator=(const volatile timepiece & rhs)
{
if(this != &rhs)
*impl_ = *rhs.impl_;
return *this;
}
void timepiece::start() volatile
{
#if defined(NANA_WINDOWS)
::QueryPerformanceCounter(&impl_->beg_timestamp);
#elif defined(NANA_LINUX)
struct timezone tz;
::gettimeofday(&impl_->beg_timestamp, &tz);
#endif
}
double timepiece::calc() const volatile
{
#if defined(NANA_WINDOWS)
LARGE_INTEGER li;
::QueryPerformanceCounter(&li);
__int64 diff = li.QuadPart - impl_->beg_timestamp.QuadPart;
LARGE_INTEGER freq;
::QueryPerformanceFrequency(&freq);
return double(diff)/double(freq.QuadPart) * 1000;
#elif defined(NANA_LINUX)
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return static_cast<double>(tv.tv_sec - impl_->beg_timestamp.tv_sec) * 1000 + static_cast<double>(tv.tv_usec - impl_->beg_timestamp.tv_usec) / 1000;
#endif
}
//end class timepiece
}//end namespace system
}//end namespace nana

399
source/threads/pool.cpp Normal file
View File

@@ -0,0 +1,399 @@
/*
* A Thread Pool Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
*
* @file: nana/threads/pool.cpp
*/
#include <nana/threads/pool.hpp>
#include <nana/system/platform.hpp>
#include <time.h>
#include <deque>
#include <vector>
#if defined(STD_THREAD_NOT_SUPPORTED)
#include <nana/std_condition_variable.hpp>
#include <nana/std_mutex.hpp>
#else
#include <condition_variable>
#include <mutex>
#endif
#if defined(NANA_WINDOWS)
#include <windows.h>
#include <process.h>
#elif defined(NANA_LINUX)
#include <pthread.h>
#endif
namespace nana
{
namespace threads
{
//class pool
//struct task
pool::task::task(t k) : kind(k){}
pool::task::~task(){}
//end struct task
//struct task_signal
struct pool::task_signal
: task
{
task_signal()
: task(task::signal)
{}
void run()
{}
};//end struct task_signal
class pool::impl
{
enum class state{init, idle, run, finished};
struct pool_throbj
{
#if defined(NANA_WINDOWS)
typedef HANDLE thread_t;
#elif defined(NANA_LINUX)
typedef pthread_t thread_t;
#endif
impl * pool_ptr;
task * task_ptr;
thread_t handle;
volatile state thr_state;
time_t timestamp;
#if defined(NANA_LINUX)
std::mutex wait_mutex;
std::condition_variable wait_cond;
volatile bool suspended;
#endif
};
public:
impl(std::size_t thr_number)
: runflag_(true)
{
if(0 == thr_number) thr_number = 4;
for(; thr_number; --thr_number)
{
pool_throbj * pto = new pool_throbj;
pto->pool_ptr = this;
pto->thr_state = state::init;
pto->task_ptr = nullptr;
#if defined(NANA_WINDOWS)
pto->handle = (HANDLE)::_beginthreadex(0, 0, reinterpret_cast<unsigned(__stdcall*)(void*)>(&impl::_m_thr_starter), pto, 0, 0);
#elif defined(NANA_LINUX)
pto->suspended = false;
::pthread_create(&(pto->handle), 0, reinterpret_cast<void*(*)(void*)>(&impl::_m_thr_starter), pto);
#endif
container_.threads.push_back(pto);
}
}
~impl()
{
runflag_ = false;
while(true)
{
bool all_finished = true;
{
for(auto thr: container_.threads)
{
if(state::finished != thr->thr_state)
{
all_finished = false;
break;
}
}
}
if(all_finished)
break;
while(true)
{
auto idle_thr = _m_pick_up_an_idle();
if(idle_thr)
_m_resume(idle_thr);
else
break;
}
nana::system::sleep(100);
}
std::vector<pool_throbj*> dup(std::move(container_.threads));
for(auto thr: dup)
{
#if defined(NANA_WINDOWS)
::WaitForSingleObject(thr->handle, INFINITE);
::CloseHandle(thr->handle);
#elif defined(NANA_LINUX)
::pthread_join(thr->handle, 0);
::pthread_detach(thr->handle);
#endif
}
std::lock_guard<decltype(mutex_)> lock(mutex_);
for(auto task_ptr : container_.tasks)
{
delete task_ptr;
}
}
void push(task * taskptr)
{
if(false == runflag_)
{
delete taskptr;
throw std::runtime_error("Nana.Pool: Do not accept task now");
}
pool_throbj * pto = _m_pick_up_an_idle();
if(pto)
{
pto->task_ptr = taskptr;
_m_resume(pto);
}
else
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
container_.tasks.push_back(taskptr);
}
}
void wait_for_signal()
{
std::unique_lock<std::mutex> lock(signal_.mutex);
signal_.cond.wait(lock);
}
void wait_for_finished()
{
while(true)
{
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
if(container_.tasks.empty())
{
bool finished = true;
for(auto thr : container_.threads)
{
if(state::run == thr->thr_state)
{
finished = false;
break;
}
}
if(finished)
return;
}
}
nana::system::sleep(100);
}
}
private:
pool_throbj* _m_pick_up_an_idle()
{
for(auto thr : container_.threads)
if(state::idle == thr->thr_state)
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
if(state::idle == thr->thr_state)
{
thr->thr_state = state::run;
return thr;
}
}
return nullptr;
}
void _m_suspend(pool_throbj* pto)
{
pto->thr_state = state::idle;
#if defined(NANA_WINDOWS)
::SuspendThread(pto->handle);
#elif defined(NANA_LINUX)
std::unique_lock<std::mutex> lock(pto->wait_mutex);
pto->suspended = true;
pto->wait_cond.wait(lock);
pto->suspended = false;
#endif
}
void _m_resume(pool_throbj* pto)
{
#if defined(NANA_WINDOWS)
while(true)
{
DWORD n = ::ResumeThread(pto->handle);
if(n == 1 || n == static_cast<DWORD>(-1))
break;
}
#elif defined(NANA_LINUX)
while(false == pto->suspended)
;
std::unique_lock<std::mutex> lock(pto->wait_mutex);
pto->wait_cond.notify_one();
#endif
}
bool _m_read(pool_throbj* pto)
{
pto->task_ptr = nullptr;
if(runflag_)
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
if(container_.tasks.size())
{
pto->task_ptr = container_.tasks.front();
container_.tasks.pop_front();
}
}
else
return false;
if(nullptr == pto->task_ptr)
{
//The task queue is empty, so that
//suspend and wait for a task.
_m_suspend(pto);
}
return (nullptr != pto->task_ptr);
}
void _m_thr_runner(pool_throbj* pto)
{
while(_m_read(pto))
{
pto->timestamp = time(nullptr);
switch(pto->task_ptr->kind)
{
case task::general:
try
{
pto->task_ptr->run();
}catch(...){}
break;
case task::signal:
while(true)
{
bool finished = true;
{
std::lock_guard<decltype(mutex_)> lock(mutex_);
for(auto thr : container_.threads)
{
if((thr != pto) && (state::run == thr->thr_state) && (thr->timestamp <= pto->timestamp))
{
finished = false;
break;
}
}
}
if(finished)
break;
nana::system::sleep(100);
}
//wait till the cond is waiting.
signal_.cond.notify_one();
break;
}
delete pto->task_ptr;
pto->task_ptr = nullptr;
}
pto->thr_state = state::finished;
}
//Here defines the a function used for creating a thread.
//This is platform-specified.
#if defined(NANA_WINDOWS)
static unsigned __stdcall _m_thr_starter(pool_throbj * pto)
{
pto->pool_ptr->_m_thr_runner(pto);
::_endthreadex(0);
return 0;
}
#elif defined(NANA_LINUX)
static void * _m_thr_starter(pool_throbj * pto)
{
pto->pool_ptr->_m_thr_runner(pto);
return nullptr;
}
#endif
private:
volatile bool runflag_;
std::recursive_mutex mutex_;
struct signal
{
std::mutex mutex;
std::condition_variable cond;
}signal_;
struct container
{
std::deque<task*> tasks;
std::vector<pool_throbj*> threads;
}container_;
};//end class impl
pool::pool()
: impl_(new impl(4))
{
}
pool::pool(std::size_t thread_number)
: impl_(new impl(thread_number))
{
}
pool::~pool()
{
delete impl_;
}
void pool::signal()
{
task * task_ptr = nullptr;
try
{
task_ptr = new task_signal;
_m_push(task_ptr);
}
catch(std::bad_alloc&)
{
delete task_ptr;
}
}
void pool::wait_for_signal()
{
impl_->wait_for_signal();
}
void pool::wait_for_finished()
{
impl_->wait_for_finished();
}
void pool::_m_push(task* task_ptr)
{
impl_->push(task_ptr);
}
//end class pool
}//end namespace threads
}//end namespace nana

20
source/traits.cpp Normal file
View File

@@ -0,0 +1,20 @@
/*
* Traits Implementation
* Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
*
* @file: source/traits.cpp
*/
#include <nana/traits.hpp>
namespace nana
{
//class noncopyable
noncopyable::noncopyable(){}
//end class noncopyable
//class nonmovable
nonmovable::nonmovable(){}
//end class nonmovable
}//end namespace nana

1098
source/unicode_bidi.cpp Normal file

File diff suppressed because it is too large Load Diff