#include "Image.hh"
#include "bbsmount.hh"
#include "tooltip.hh"
#include "i18n.hh"
#define MAX_EXTENT max_logical_extent
//#define MAX_EXTENT max_ink_extent
int
timeval_subtract (const struct timeval *time1, const struct timeval *time0, struct timeval *result) {
result->tv_sec = time1->tv_sec - time0->tv_sec;
result->tv_usec = time1->tv_usec - time0->tv_usec;
if (result->tv_usec < 0) {
result->tv_sec -= 1 - result->tv_usec / 1000000;
result->tv_usec = 1000000 - (-result->tv_usec) % 1000000;
}
return (result->tv_sec < 0 || (result->tv_sec == 0 && result->tv_usec < 0));
}
#ifdef DELAYED_TOOLTIPS
void *
waiter_thread_helper_function(void *waiter_object) {
Waiter *waiter = (Waiter *)waiter_object;
waiter->waiter_loop();
return NULL;
}
Waiter::Waiter() {
int result;
end = fire = false;
wait_secs = 0;
wait_from.tv_sec = 0;
wait_from.tv_usec = 0;
func = NULL;
param = NULL;
result = sem_init(&run_waiter, 0, 0);
if (result != 0) {
perror("bbsmount: Can't initialize semaphore!");
exit(EXIT_FAILURE);
}
result = pthread_create(&wait_thread, NULL, waiter_thread_helper_function, (void *)this);
if (result != 0) {
perror("bbsmount: Can't create thread!");
exit(EXIT_FAILURE);
}
}
Waiter::~Waiter() {
int result;
fire = false;
end = true;
sem_post(&run_waiter);
result = pthread_join(wait_thread, NULL);
if (result != 0) {
perror("bbsmount: Can't join thread!");
exit(EXIT_FAILURE);
}
sem_destroy(&run_waiter);
}
void
Waiter::waiter_loop(void) {
struct timeval current_time, difference, wait_to;
struct timespec sleep_time;
while (true) {
if (end)
pthread_exit(NULL);
if (fire) {
if (gettimeofday(¤t_time, NULL) != 0) {
perror("bbsmount: Can't get current time!");
return;
}
wait_to.tv_sec = wait_from.tv_sec + wait_secs;
wait_to.tv_usec = wait_from.tv_usec;
if (timeval_subtract(&wait_to, ¤t_time, &difference)) {
fire = false;
((*param).*func)();
} else {
sleep_time.tv_sec = difference.tv_sec;
sleep_time.tv_nsec = difference.tv_usec * 1000;
nanosleep(&sleep_time, NULL);
}
}
else
sem_wait(&run_waiter);
}
}
void
Waiter::wait(const int _secs, void (BToolTip::*_func)(), BToolTip *_param) {
if (gettimeofday(&wait_from, NULL) != 0) {
perror("bbsmount: Can't get current time!");
return;
}
wait_secs = _secs;
func = _func;
param = _param;
fire = true;
sem_post(&run_waiter);
}
void
Waiter::cancel(void) {
fire = false;
}
#endif /* DELAYED_TOOLTIPS */
BToolTip::BToolTip() {
is_visible = is_created = false;
real_x = real_y = x = y = timeout = 0;
real_w = real_h = 0;
direction = bottom_right;
tooltip_window = None;
tooltip_pixmap = None;
pthread_mutex_init(&show_hide_syn, (pthread_mutexattr_t *)NULL);
}
BToolTip::~BToolTip() {
hide();
if (tooltip_pixmap != None)
AppWindow->getImageControl()->removeImage(tooltip_pixmap);
if (tooltip_window != None)
XDestroyWindow(AppWindow->getXDisplay(), tooltip_window);
pthread_mutex_destroy(&show_hide_syn);
}
void
BToolTip::createToolTipWindow(const int _x, const int _y, const unsigned int _w, const unsigned int _h) {
pthread_mutex_lock(&show_hide_syn);
if (!is_created) {
XSetWindowAttributes attrib;
unsigned long mask = CWBorderPixel | CWColormap | CWSaveUnder | CWOverrideRedirect | CWEventMask;
attrib.border_pixel = AppWindow->GetResources()->getTooltipBorderColor().getPixel();
attrib.colormap = AppWindow->getImageControl()->getColormap();
attrib.override_redirect = True;
attrib.save_under = True;
attrib.event_mask = ExposureMask;
tooltip_window = XCreateWindow(AppWindow->getXDisplay(), DefaultRootWindow(AppWindow->getXDisplay()),
_x, _y, _w, _h, AppWindow->GetResources()->getTooltipBorderWidth(),
AppWindow->getImageControl()->getDepth(), InputOutput, AppWindow->getCurrentScreenInfo()->getVisual(),
mask, &attrib);
} else
XMoveResizeWindow(AppWindow->getXDisplay(), tooltip_window, _x, _y, _w, _h);
XMapWindow(AppWindow->getXDisplay(), tooltip_window);
XRaiseWindow(AppWindow->getXDisplay(), tooltip_window);
is_visible = true;
if (!is_created || _w != real_w || _h != real_h) {
BTexture *texture = AppWindow->GetResources()->getTooltipTexture();
if (tooltip_pixmap != None)
AppWindow->getImageControl()->removeImage(tooltip_pixmap);
tooltip_pixmap = AppWindow->getImageControl()->renderImage(_w, _h, texture);
// if (tooltip_pixmap == ParentRelative) {
// texture = &(resource.wstyle.t_focus);
// geom_pixmap = texture->render(geom_w, geom_h, geom_pixmap);
// }
if (!tooltip_pixmap)
XSetWindowBackground(AppWindow->getXDisplay(), tooltip_window, AppWindow->GetResources()->getTooltipBorderColor().getPixel());
else
XSetWindowBackgroundPixmap(AppWindow->getXDisplay(), tooltip_window, tooltip_pixmap);
}
real_w = _x;
real_y = _y;
real_w = _w;
real_h = _h;
is_created = true;
pthread_mutex_unlock(&show_hide_syn);
}
void
BToolTip::setText(const string &tooltip_text) {
unsigned int found, last_found = 0;
current_tooltip_text.clear();
while ((found = tooltip_text.find('\n', last_found)) != string::npos) {
current_tooltip_text.push_back(tooltip_text.substr(last_found, (found - last_found)));
last_found = found + 1;
}
if (last_found < tooltip_text.length())
current_tooltip_text.push_back(tooltip_text.substr(last_found));
if (is_visible)
showNow();
}
void
BToolTip::setTimeout(const int seconds) {
#ifdef DELAYED_TOOLTIPS
timeout = seconds;
#else /* !DELAYED_TOOLTIPS */
timeout = 0;
#endif /* !DELAYED_TOOLTIPS */
}
void
BToolTip::setPosition(const int _x, const int _y, const direction_type _direction) {
x = _x;
y = _y;
direction = _direction;
if (is_visible)
show();
}
void
BToolTip::redraw(const int _x, const int _y, const unsigned int _w, const unsigned int _h) {
int line = 0, y_start, y_end;
vector<string>::const_iterator pointer;
XGCValues values;
if (!i18n.multibyte())
values.font = AppWindow->GetResources()->getTooltipFont()->fid;
GC gctt = XCreateGC(AppWindow->getXDisplay(), tooltip_window, (i18n.multibyte() ? 0 : GCFont), &values);
XClearArea(AppWindow->getXDisplay(), tooltip_window, _x, _y, _w, _h, False);
for (pointer = current_tooltip_text.begin(); pointer != current_tooltip_text.end(); pointer++) {
if (i18n.multibyte()) {
y_start = AppWindow->GetResources()->getTooltipBevelWidth() -
AppWindow->GetResources()->getTooltipFontSetExtents()->MAX_EXTENT.y +
AppWindow->GetResources()->getTooltipFontSetExtents()->MAX_EXTENT.height * line++;
y_end = y_start + AppWindow->GetResources()->getTooltipFontSetExtents()->MAX_EXTENT.height;
}
else {
y_start = AppWindow->GetResources()->getTooltipFont()->ascent +
AppWindow->GetResources()->getTooltipBevelWidth() +
(AppWindow->GetResources()->getTooltipFont()->ascent +
AppWindow->GetResources()->getTooltipFont()->descent) * line++;
y_end = y_start + (AppWindow->GetResources()->getTooltipFont()->ascent +
AppWindow->GetResources()->getTooltipFont()->descent);
}
if (_y >= y_end)
continue;
if (_y + (int)_h < y_start)
break;
if (i18n.multibyte()) {
XmbDrawString(AppWindow->getXDisplay(), tooltip_window,
AppWindow->GetResources()->getTooltipFontSet(), gctt,
AppWindow->GetResources()->getTooltipBevelWidth(), y_start,
pointer->c_str(), pointer->length());
} else {
XDrawString(AppWindow->getXDisplay(), tooltip_window,
gctt, AppWindow->GetResources()->getTooltipBevelWidth(), y_start,
pointer->c_str(), pointer->length());
}
}
XFlush(AppWindow->getXDisplay());
XFreeGC(AppWindow->getXDisplay(), gctt);
}
void
BToolTip::processEvent(const XEvent &event) {
if (event.type == Expose && event.xexpose.window == tooltip_window)
redraw(event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height);
}
void
BToolTip::hide(void) {
#ifdef DELAYED_TOOLTIPS
waiter.cancel();
#endif /* DELAYED_TOOLTIPS */
pthread_mutex_lock(&show_hide_syn);
if (is_visible) {
is_visible = false;
XUnmapWindow(AppWindow->getXDisplay(), tooltip_window);
current_tooltip_text.clear(); // destroy tooltip content
real_w = real_h = 0;
}
pthread_mutex_unlock(&show_hide_syn);
}
void
BToolTip::show(void) {
#ifdef DELAYED_TOOLTIPS
if (timeout > 0)
waiter.wait(timeout, &BToolTip::showNow, this);
else
#endif /* DELAYED_TOOLTIPS */
showNow();
}
void
BToolTip::showNow(void) {
unsigned int text_width, text_height, width, height, max_width = 0;
int new_x = x, new_y = y;
vector<string>::const_iterator pointer;
if (current_tooltip_text.empty())
return;
if (i18n.multibyte()) {
for (pointer = current_tooltip_text.begin(); pointer != current_tooltip_text.end(); pointer++) {
text_width = XmbTextEscapement(AppWindow->GetResources()->getTooltipFontSet(),
pointer->c_str(), pointer->length());
if (text_width > max_width)
max_width = text_width;
}
} else {
for (pointer = current_tooltip_text.begin(); pointer != current_tooltip_text.end(); pointer++) {
text_width = XTextWidth(AppWindow->GetResources()->getTooltipFont(),
pointer->c_str(), pointer->length());
if (text_width > max_width)
max_width = text_width;
}
}
text_width = max_width;
if (i18n.multibyte())
text_height = AppWindow->GetResources()->getTooltipFontSetExtents()->MAX_EXTENT.height * current_tooltip_text.size();
else
text_height = (AppWindow->GetResources()->getTooltipFont()->ascent +
AppWindow->GetResources()->getTooltipFont()->descent) * current_tooltip_text.size();
width = text_width + 2 * (AppWindow->GetResources()->getTooltipBevelWidth() + AppWindow->GetResources()->getTooltipBorderWidth());
height = text_height + 2 * (AppWindow->GetResources()->getTooltipBevelWidth() + AppWindow->GetResources()->getTooltipBorderWidth());
switch (direction) {
case top_left:
new_x = x - width;
if (new_x < 0)
new_x = 0;
new_y = y - height;
if (new_y < 0)
new_y = 0;
break;
case top_right:
if (new_x + width >= AppWindow->getCurrentScreenInfo()->getWidth())
new_x = AppWindow->getCurrentScreenInfo()->getWidth() - width;
new_y = y - height;
if (new_y < 0)
new_y = 0;
break;
case bottom_left:
new_x = x - width;
if (new_x < 0)
new_x = 0;
if (new_y + height >= AppWindow->getCurrentScreenInfo()->getHeight())
new_y = AppWindow->getCurrentScreenInfo()->getHeight() - height;
break;
case bottom_right:
if (new_x + width >= AppWindow->getCurrentScreenInfo()->getWidth())
new_x = AppWindow->getCurrentScreenInfo()->getWidth() - width;
if (new_y + height >= AppWindow->getCurrentScreenInfo()->getHeight())
new_y = AppWindow->getCurrentScreenInfo()->getHeight() - height;
break;
}
createToolTipWindow(new_x, new_y, width, height);
redraw(0, 0, width, height);
}
bool
BToolTip::isShown(void) const {
return is_visible;
}
int
BToolTip::getTimeout(void) const {
return timeout;
}
syntax highlighted by Code2HTML, v. 0.9.1