ede/edewm/Titlebar.cpp
2007-07-18 18:20:04 +00:00

430 lines
8.7 KiB
C++

/*
* $Id$
*
* Edewm, window manager
* Part of Equinox Desktop Environment (EDE).
* Copyright (c) 2000-2006 EDE Authors.
*
* This program is licenced under terms of the
* GNU General Public Licence version 2 or newer.
* See COPYING for details.
*/
#include "Titlebar.h"
#include "Frame.h"
#include "debug.h"
#include <efltk/fl_draw.h>
#include <efltk/Fl.h>
#include <efltk/Fl_Image.h>
#include <efltk/Fl_Locale.h>
#include <assert.h>
#include "app.xpm"
Fl_Image app_img((const char**)app_xpm);
#define MAX_OF 0.6f
#define MIN_OF 0.5f
#define RES_OF 0.1f
#define vv(x,y) fl_vertex(x,y)
void draw_close(Fl_Color col)
{
fl_rotate(45);
fl_color(col);
vv(-0.9f,-0.12f); vv(-0.9f,0.12f); vv(0.9f,0.12f); vv(0.9f,-0.12f); fl_fill_stroke(FL_DARK3);
vv(-0.12f,-0.9f); vv(-0.12f,0.9f); vv(0.12f,0.9f); vv(0.12f,-0.9f); fl_fill_stroke(FL_DARK3);
}
void draw_max(Fl_Color col)
{
fl_color(col);
vv(-MAX_OF, -MAX_OF); vv(MAX_OF, -MAX_OF);
vv(MAX_OF,-MAX_OF+0.4); vv(-MAX_OF,-MAX_OF+0.4);
fl_fill();
vv(MAX_OF,-MAX_OF); vv(MAX_OF,MAX_OF);
vv(-MAX_OF,MAX_OF); vv(-MAX_OF,-MAX_OF);
fl_stroke();
}
void draw_maximized(Fl_Color col)
{
fl_color(col);
vv(-MAX_OF+RES_OF, -MAX_OF+RES_OF); vv(MAX_OF-RES_OF, -MAX_OF+RES_OF);
vv(MAX_OF-RES_OF,-MAX_OF+RES_OF); vv(MAX_OF-RES_OF,MAX_OF-RES_OF);
vv(-MAX_OF+RES_OF,MAX_OF-RES_OF); vv(-MAX_OF+RES_OF,-MAX_OF+RES_OF);
fl_stroke();
vv(-MAX_OF+RES_OF+0.2f, -MAX_OF+RES_OF+0.2f); vv(MAX_OF-RES_OF-0.2f, -MAX_OF+RES_OF+0.2f);
vv(MAX_OF-RES_OF-0.2f,-MAX_OF+RES_OF+0.2f); vv(MAX_OF-RES_OF-0.2f,MAX_OF-RES_OF-0.2f);
vv(-MAX_OF+RES_OF+0.2f,MAX_OF-RES_OF-0.2f); vv(-MAX_OF+RES_OF+0.2f,-MAX_OF+RES_OF+0.2f);
fl_stroke();
}
void draw_min(Fl_Color col)
{
fl_color(col);
vv(-MIN_OF, MIN_OF); vv(MIN_OF, MIN_OF);
vv(MIN_OF, MIN_OF+0.3f); vv(-MIN_OF, MIN_OF+0.3f);
fl_fill();
}
void close_cb(Fl_Widget*, void* t)
{
Titlebar* tb = (Titlebar*)t;
assert(tb != NULL);
tb->on_close();
}
void maximize_cb(Fl_Widget* b, void* t)
{
Titlebar* tb = (Titlebar*)t;
assert(tb != NULL);
tb->on_maximize();
}
void shade_cb(Fl_Widget*, void* t)
{
Titlebar* tb = (Titlebar*)t;
assert(tb != NULL);
tb->on_shade();
}
void minimize_cb(Fl_Widget*, void* t)
{
Titlebar* tb = (Titlebar*)t;
assert(tb != NULL);
tb->on_minimize();
}
void lower_cb(Fl_Widget*, void* t)
{
Titlebar* tb = (Titlebar*)t;
assert(tb != NULL);
tb->on_lower();
}
TitlebarButton::TitlebarButton(int type) :
Fl_Button(0, 0, 0, 0, 0),
button_type(type),
pos(PLACE_RIGHT)
{
focus_box(FL_NO_BOX);
box(FL_FLAT_BOX);
label_type(FL_SYMBOL_LABEL);
switch(button_type)
{
case TITLEBAR_CLOSE_UP:
label("@xx");
break;
case TITLEBAR_MAX_UP:
label("@mx");
break;
case TITLEBAR_MIN_UP:
label("@ii");
break;
}
}
TitlebarButton::~TitlebarButton()
{
}
/* Place buttons on titlebar. The can be either
* PLACE_RIGHT (standard in many wm-s) or PLACE_RIGHT
*/
void TitlebarButton::place(int p)
{
if(p != PLACE_LEFT && p != PLACE_RIGHT)
pos = PLACE_RIGHT;
else
pos = p;
}
Titlebar::Titlebar(Frame* f, int x, int y, int w, int h, const char* l) :
Fl_Group(x, y, w, h, l),
win_x(0),
win_y(0),
win_x1(0),
win_y1(0),
curr_frame(f),
minb(TITLEBAR_MIN_UP),
maxb(TITLEBAR_MAX_UP),
closeb(TITLEBAR_CLOSE_UP),
label_box(new Fl_Box(0, 0, 0, 0)),
icon_box(new Fl_Box(0, 0, 0, 0)),
focus_color(FL_GRAY),
unfocus_color(FL_WHITE)
{
assert(curr_frame != NULL);
box(FL_FLAT_BOX);
fl_add_symbol("xx", draw_close, 1);
fl_add_symbol("mx", draw_max, 1);
fl_add_symbol("mz", draw_maximized, 1);
fl_add_symbol("ii", draw_min, 1);
label_box->box(FL_FLAT_BOX);
label_box->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
label_box->color(FL_GRAY);
icon_box->image(app_img);
title_menu = new Fl_Menu_();
/* XXX: any way this can be better ?
* When we change label of existing menu item, menu width will not be
* recalculated honoring size of that item. So we must initially fill
* them with blanks, which will keep longer labels fully visible.
*/
menu_max = title_menu->add(_("Maximize "), 0, maximize_cb, this);
menu_shade = title_menu->add(_("Shade "), 0, shade_cb, this);
menu_lower = title_menu->add(_("Lower "), 0, lower_cb, this);
menu_min = title_menu->add(_("Minimize "), 0, minimize_cb, this, FL_MENU_DIVIDER);
menu_close = title_menu->add(_("Close "), 0, close_cb, this);
//closeb.place(PLACE_LEFT);
end();
closeb.callback(close_cb, this);
//minb.callback(minimize_cb, this);
minb.callback(shade_cb, this);
maxb.callback(maximize_cb, this);
color(FL_GRAY);
//align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
if(curr_frame->frame_type(FrameTypeDialog))
{
minb.hide();
maxb.hide();
}
}
Titlebar::~Titlebar()
{
}
#define PLACE_BUTTON(btn, leftx, rightx, mid, offset, sz) \
if(btn.place() == PLACE_LEFT) \
{\
btn.resize(leftx, mid, sz, sz); \
if(btn.visible()) \
leftx += sz + offset; \
}\
if(btn.place() == PLACE_RIGHT) \
{\
btn.resize(rightx, mid, sz, sz); \
if(btn.visible()) \
rightx -= sz + offset; \
}
void Titlebar::layout(void)
{
int initial_offset = 2; // distance from borders
int W = w() - box()->dx() - initial_offset;
int X = box()->dx() + initial_offset;
int lsize = h() / 2 + 2;
int offset = 0;
int mid = 0;
int sz = h();
label_size(lsize);
sz -= 4; // button width/height
mid = 2;
offset = 2;
int lx = X + offset; // left x
int rx = W - sz - offset; // right x
PLACE_BUTTON(closeb, lx, rx, mid, offset, sz)
PLACE_BUTTON(maxb, lx, rx, mid, offset, sz)
PLACE_BUTTON(minb, lx, rx, mid, offset, sz)
//rx -= sz + offset;
//lx += sz + offset;
if(icon_box->visible())
{
icon_box->resize(lx, mid, sz, sz);
lx += sz + offset;
}
lx += offset;
rx -= lx;
label_box->resize(lx, mid, rx, sz);
fl_font(label_font(), label_size());
// take a label from titlebar object
if(!label().empty())
label_box->label(label());
Fl_Group::layout();
}
void Titlebar::focus(void)
{
// TODO: add colors for label_box, buttons
color(focus_color);
redraw();
}
void Titlebar::unfocus(void)
{
// TODO: add colors for label_box, buttons
color(unfocus_color);
redraw();
}
void Titlebar::on_close(void)
{
assert(curr_frame != NULL);
curr_frame->close_kill();
}
void Titlebar::on_maximize(void)
{
assert(curr_frame != NULL);
if(curr_frame->state(FrameStateMaximized))
{
curr_frame->restore();
maxb.label("@mx");
menu_max->label(_("Maximize"));
}
else
{
curr_frame->maximize();
maxb.label("@mz");
menu_max->label(_("UnMaximize"));
}
}
void Titlebar::on_minimize(void)
{
assert(curr_frame != NULL);
// TODO: not implemented
}
void Titlebar::on_shade(void)
{
assert(curr_frame != NULL);
if(curr_frame->state(FrameStateShaded))
{
curr_frame->unshade();
menu_shade->label(_("Shade"));
}
else
{
curr_frame->shade();
menu_shade->label(_("UnShade"));
}
}
void Titlebar::on_lower(void)
{
curr_frame->lower();
}
int Titlebar::handle(int event)
{
// check if event is directed to our buttons
if(event == FL_PUSH || event == FL_RELEASE)
{
Fl_Widget* ch = 0;
for(int i = 0; i < children(); i++)
{
ch = child(i);
// do not send event to label_box
if(label_box && ch == label_box)
continue;
if(ch && Fl::event_inside(ch->x(), ch->y(), ch->w(), ch->h()))
{
ch->send(event);
return 1;
}
}
}
// proceed to our events
switch(event)
{
case FL_PUSH:
ELOG("Titlebar FL_PUSH");
if(Fl::event_state(FL_BUTTON3))
{
title_menu->popup(Fl::event_x_root(), Fl::event_y_root());
title_menu->redraw();
return 1;
}
// prevent double click
if(Fl::event_is_click())
curr_frame->raise();
win_x = Fl::event_x_root() - curr_frame->x();
win_y = Fl::event_y_root() - curr_frame->y();
return 1;
case FL_DRAG:
ELOG("Titlebar FL_DRAG");
// TODO: correct behaviour ???
if(curr_frame->state(FrameStateMaximized))
return 1;
win_x1 = Fl::event_x_root() - win_x;
win_y1 = Fl::event_y_root() - win_y;
if(!curr_frame->moving())
{
curr_frame->move_start();
/* We can safely call grab_cursor() as many times
* as we want, since it have internal checks
*
* TODO: add XGrabServer() here
*/
curr_frame->set_cursor(CURSOR_MOVE);
curr_frame->grab_cursor();
curr_frame->show_coordinates_window();
}
curr_frame->move_window(win_x1, win_y1);
curr_frame->update_coordinates_window();
return 1;
case FL_RELEASE:
ELOG("Titlebar FL_RELEASE");
if(curr_frame->moving())
{
curr_frame->ungrab_cursor();
curr_frame->set_cursor(CURSOR_DEFAULT);
curr_frame->move_end();
curr_frame->hide_coordinates_window();
}
return 1;
case FL_ENTER:
ELOG("Titlebar::FL_ENTER");
return 1;
case FL_LEAVE:
ELOG("Titlebar::FL_LEAVE");
return 1;
default:
return Fl_Group::handle(event);
}
return 0;
}