/*************************************************************************** gmainwindow.cpp (c) 2004-2006 - Daniel Campos Fernández Gtkmae "GTK+ made easy" classes Realizado para la Junta de Extremadura. Consejería de Educación Ciencia y Tecnología. Proyecto gnuLinEx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #include #include #include #include "widgets.h" #include "widgets_private.h" #ifdef GDK_WINDOWING_X11 #include #endif #include "gapplication.h" #include "gdesktop.h" #include "gkey.h" #include "gmenu.h" #include "gmessage.h" #include "gdialog.h" #include "gmainwindow.h" static gboolean win_frame(GtkWidget *widget,GdkEventWindowState *event,gMainWindow *data) { data->performArrange(); return false; } static void cb_show (GtkWidget *widget, gMainWindow *data) { //fprintf(stderr, "cb_show: %p\n", data); //data->afterShow(); data->emitOpen(); if (data->opened) { //data->performArrange(); data->emit(SIGNAL(data->onShow)); data->_not_spontaneous = false; } } static void cb_hide (GtkWidget *widget, gMainWindow *data) { data->emit(SIGNAL(data->onHide)); data->_not_spontaneous = false; if (data == gDesktop::activeWindow()) gMainWindow::setActiveWindow(NULL); } static gboolean win_close(GtkWidget *widget,GdkEvent *event,gMainWindow *data) { data->doClose(); return true; } static gboolean resize_later(gMainWindow *data) { data->bufW = data->_next_w; data->bufH = data->_next_h; data->_next_timer = 0; data->performArrange(); if (data->onResize) data->onResize(data); data->refresh(); return false; } static gboolean cb_configure(GtkWidget *widget, GdkEventConfigure *event, gMainWindow *data) { gint x, y; //fprintf(stderr, "cb_configure: %p\n", data); if (data->opened) { if (data->isTopLevel()) gtk_window_get_position(GTK_WINDOW(data->border), &x, &y); else { x = event->x; y = event->y; } if (x != data->bufX || y != data->bufY) { data->bufX = x; data->bufY = y; if (data->onMove) data->onMove(data); } if ( (event->width!=data->bufW) || (event->height!=data->bufH) || (data->_resized) || !event->window ) { data->_next_w = event->width; data->_next_h = event->height; /* data->bufW=event->width; data->bufH=event->height; //g_debug("gMainWindow: cb_configure: %d", event->send_event); data->performArrange(); if (data->onResize) data->onResize(data);*/ //if (data->_resized || data->parent()) { data->_resized = false; resize_later(data); } //else //{ // //resize_later(data); // if (!data->_next_timer) // data->_next_timer = g_timeout_add(50, (GSourceFunc)resize_later, data); //} } } return false; } static gboolean cb_open(GtkWidget *widget, GdkEvent *e, gMainWindow *data) { data->emitOpen(); return FALSE; } static gboolean cb_map(GtkWidget *widget, GdkEvent *e, gMainWindow *data) { data->afterShow(); return FALSE; } static gboolean cb_expose(GtkWidget *wid, GdkEventExpose *e, gMainWindow *data) { if (data->_background) { gdk_window_clear(data->border->window); gdk_window_clear(GTK_LAYOUT(data->widget)->bin_window); } return false; } GList *gMainWindow::windows = NULL; gMainWindow *gMainWindow::_active = NULL; gMainWindow *gMainWindow::_current = NULL; static GtkWindowGroup *_current_group = NULL; void gMainWindow::initialize() { //fprintf(stderr, "new window: %p in %p\n", this, parent()); opened = false; sticky = false; persistent = false; stack = 0; _mask = false; _masked = false; _resized = false; accel = NULL; _default = NULL; _cancel = NULL; menuBar = NULL; top_only = false; _icon = NULL; _picture = NULL; focus = 0; _closing = false; _title = NULL; _toolbox = false; _not_spontaneous = false; _skip_taskbar = false; _current = NULL; _background = NULL; _style = NULL; _next_timer = 0; _xembed = false; _activate = false; _hidden = false; onOpen = NULL; onShow = NULL; onHide = NULL; onMove = NULL; onResize = NULL; onActivate = NULL; onDeactivate = NULL; accel = gtk_accel_group_new(); } void gMainWindow::initWindow() { //resize(200,150); if (!isTopLevel()) { g_signal_connect(G_OBJECT(border), "show", G_CALLBACK(cb_show), (gpointer)this); g_signal_connect(G_OBJECT(border), "configure-event", G_CALLBACK(cb_configure), (gpointer)this); g_signal_connect_after(G_OBJECT(border), "map-event", G_CALLBACK(cb_open), (gpointer)this); g_signal_connect(G_OBJECT(border),"show",G_CALLBACK(cb_show),(gpointer)this); g_signal_connect(G_OBJECT(border),"hide",G_CALLBACK(cb_hide),(gpointer)this); //g_signal_connect_after(G_OBJECT(border), "size-allocate", G_CALLBACK(cb_configure), (gpointer)this); g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(cb_expose), (gpointer)this); } else { //g_signal_connect(G_OBJECT(border),"size-request",G_CALLBACK(cb_realize),(gpointer)this); g_signal_connect(G_OBJECT(border), "show",G_CALLBACK(cb_show),(gpointer)this); g_signal_connect(G_OBJECT(border), "hide",G_CALLBACK(cb_hide),(gpointer)this); g_signal_connect(G_OBJECT(border), "configure-event",G_CALLBACK(cb_configure),(gpointer)this); g_signal_connect(G_OBJECT(border), "delete-event",G_CALLBACK(win_close),(gpointer)this); g_signal_connect(G_OBJECT(border), "window-state-event",G_CALLBACK(win_frame),(gpointer)this); g_signal_connect_after(G_OBJECT(border), "map-event", G_CALLBACK(cb_map), (gpointer)this); gtk_widget_add_events(widget,GDK_BUTTON_MOTION_MASK); g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(cb_expose), (gpointer)this); } //GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); //gtk_widget_add_events(widget, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); /* GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_POINTER_MOTION_MASK);*/ gtk_window_add_accel_group(GTK_WINDOW(topLevel()->border), accel); have_cursor = true; //parent() == 0 && !_xembed; } gMainWindow::gMainWindow(int plug) : gContainer(NULL) { initialize(); g_typ = Type_gMainWindow; windows = g_list_append(windows, (gpointer)this); _xembed = plug != 0; if (_xembed) border = gtk_plug_new(plug); else border = gtk_window_new(GTK_WINDOW_TOPLEVEL); widget = gtk_layout_new(0,0); realize(false); initWindow(); gtk_widget_realize(border); gtk_widget_show(widget); gtk_widget_set_size_request(border, 4, 4); } gMainWindow::gMainWindow(gContainer *par) : gContainer(par) { initialize(); g_typ = Type_gMainWindow; border = gtk_event_box_new(); widget = gtk_layout_new(0,0); realize(false); initWindow(); } gMainWindow::~gMainWindow() { //fprintf(stderr, "delete window %p %s\n", this, name()); if (opened) { emit(SIGNAL(onClose)); opened = false; if (GTK_IS_WINDOW(border) && modal()) gApplication::exitLoop(); } if (_next_timer) g_source_remove(_next_timer); gPicture::assign(&_picture); gPicture::assign(&_icon); if (_title) g_free(_title); g_object_unref(accel); if (_background) g_object_unref(_background); if (_style) g_object_unref(_style); windows = g_list_remove(windows, (gpointer)this); } bool gMainWindow::getSticky() { return sticky; } int gMainWindow::getStacking() { return stack; } void gMainWindow::setSticky(bool vl) { sticky=vl; if (!isTopLevel()) return; if (vl) gtk_window_stick(GTK_WINDOW(border)); else gtk_window_unstick(GTK_WINDOW(border)); } void gMainWindow::setStacking(int vl) { stack=vl; if (!isTopLevel()) return; switch (vl) { case 0: gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); break; case 1: gtk_window_set_keep_below(GTK_WINDOW(border),FALSE); gtk_window_set_keep_above(GTK_WINDOW(border),TRUE); break; case 2: gtk_window_set_keep_above(GTK_WINDOW(border),FALSE); gtk_window_set_keep_below(GTK_WINDOW(border),TRUE); break; } } void gMainWindow::setRealBackground(gColor color) { if (!_picture) gControl::setRealBackground(color); } void gMainWindow::move(int x, int y) { if (isTopLevel()) { if (x == bufX && y == bufY) return; bufX = x; bufY = y; gtk_window_move(GTK_WINDOW(border), x, y); } else { gContainer::move(x,y); } } void gMainWindow::resize(int w, int h) { if (isTopLevel()) { if (w == bufW && h == bufH) return; _resized=true; bufW = w; bufH = h; //gdk_window_enable_synchronized_configure (border->window); if (gtk_window_get_resizable(GTK_WINDOW(border))) gtk_window_resize(GTK_WINDOW(border),w,h); else gtk_widget_set_size_request(border, w, h); } else { //fprintf(stderr, "resize %p -> (%d %d) (%d %d)\n", this, bufW, bufH, w, h); gContainer::resize(w, h); } } void gMainWindow::emitOpen() { if (!opened) { //fprintf(stderr, "emit Open: %p (%d %d)\n", this, width(), height()); opened = true; gtk_widget_realize(border); performArrange(); emit(SIGNAL(onOpen)); if (opened) { //fprintf(stderr, "emit Move & Resize: %p\n", this); emit(SIGNAL(onMove)); emit(SIGNAL(onResize)); } } } void gMainWindow::afterShow() { if (_activate) { gtk_window_present(GTK_WINDOW(border)); _activate = false; } } void gMainWindow::setVisible(bool vl) { GtkWindowGroup *group; if (vl) { emitOpen(); if (!opened) return; if (isTopLevel() && !_xembed) { group = _current_group; if (!group) group = gtk_window_get_group(NULL); //fprintf(stderr, "adding window %p to group %p\n", this, group); gtk_window_group_add_window(group, GTK_WINDOW(border)); } _not_spontaneous = !visible; visible = true; _hidden = false; if (isTopLevel()) { if (!_title || !*_title) gtk_window_set_title(GTK_WINDOW(border), gApplication::defaultTitle()); gtk_window_present(GTK_WINDOW(border)); gtk_window_move(GTK_WINDOW(border), bufX, bufY); } else { gtk_widget_show(border); parent()->performArrange(); } drawMask(); if (focus) { focus->setFocus(); focus = 0; } if (skipTaskBar()) _activate = true; } else { if (this == _active) focus = gDesktop::activeControl(); _not_spontaneous = visible; _hidden = true; gControl::setVisible(false); } } void gMainWindow::setMinimized(bool vl) { if (vl) gtk_window_iconify(GTK_WINDOW(border)); else gtk_window_deiconify(GTK_WINDOW(border)); } void gMainWindow::setMaximized(bool vl) { if (vl) gtk_window_maximize(GTK_WINDOW(border)); else gtk_window_unmaximize(GTK_WINDOW(border)); } void gMainWindow::setFullscreen(bool vl) { if (vl) gtk_window_fullscreen(GTK_WINDOW(border)); else gtk_window_unfullscreen(GTK_WINDOW(border)); } bool gMainWindow::minimized() { return (bool)(gdk_window_get_state(border->window) & GDK_WINDOW_STATE_ICONIFIED); } bool gMainWindow::maximized() { return (bool)(gdk_window_get_state(border->window) & GDK_WINDOW_STATE_MAXIMIZED); } bool gMainWindow::fullscreen() { return (bool)(gdk_window_get_state(border->window) & GDK_WINDOW_STATE_FULLSCREEN); } void gMainWindow::center() { int myx,myy; if (!isTopLevel()) return; myx=(gDesktop::width()/2)-(width()/2); myy=(gDesktop::height()/2)-(height()/2); move(myx,myy); } bool gMainWindow::modal() { if (!isTopLevel()) return false; return gtk_window_get_modal (GTK_WINDOW(border)); } void gMainWindow::showModal() { gMainWindow *save; GtkWindowGroup *save_group; gMainWindow *active; if (!isTopLevel()) return; if (modal()) return; save = _current; _current = this; active = gDesktop::activeWindow(); if (active && active != this) gtk_window_set_transient_for(GTK_WINDOW(border), GTK_WINDOW(active->border)); gtk_window_set_modal(GTK_WINDOW(border), true); center(); show(); save_group = _current_group; _current_group = gtk_window_group_new(); //fprintf(stderr, "showModal: begin %p\n", this); gApplication::enterLoop(); //fprintf(stderr, "showModal: end %p\n", this); g_object_unref(_current_group); _current_group = save_group; _current = save; gtk_window_set_modal(GTK_WINDOW(border), false); if (!persistent) destroyNow(); else hide(); } void gMainWindow::raise() { if (!isTopLevel()) { gControl::raise(); return; } gtk_window_present(GTK_WINDOW(border)); } const char* gMainWindow::text() { return _title; } bool gMainWindow::skipTaskBar() { if (!isTopLevel()) return false; else return _skip_taskbar; } void gMainWindow::setText(const char *txt) { if (_title) g_free(_title); _title = g_strdup(txt); if (isTopLevel()) gtk_window_set_title(GTK_WINDOW(border), txt); } int gMainWindow::getBorder() { if (!isTopLevel()) return 0; if (!gtk_window_get_decorated(GTK_WINDOW(border))) return 0; if (!gtk_window_get_resizable(GTK_WINDOW(border))) return 1; return 2; } void gMainWindow::setBorder(int b) { if (!isTopLevel()) return; switch (b) { case 0: // None gtk_window_set_decorated(GTK_WINDOW(border),false); break; case 1: // Fixed gtk_window_set_decorated(GTK_WINDOW(border),true); if (gtk_window_get_resizable(GTK_WINDOW(border))) { gtk_window_set_resizable(GTK_WINDOW(border),false); gtk_widget_set_size_request(border,bufW,bufH); } break; case 2: // Resizable gtk_window_set_decorated(GTK_WINDOW(border),true); if (!gtk_window_get_resizable(GTK_WINDOW(border))) { gtk_window_set_resizable(GTK_WINDOW(border),true); gtk_widget_set_size_request(border,1,1); } break; } } void gMainWindow::setSkipTaskBar(bool b) { _skip_taskbar = b; if (!isTopLevel()) return; gtk_window_set_skip_taskbar_hint (GTK_WINDOW(border), b); } /*gPicture* gMainWindow::icon() { GdkPixbuf *buf; gPicture *pic; if (parent()) return NULL; buf=gtk_window_get_icon(GTK_WINDOW(border)); if (!buf) return NULL; pic=gPicture::fromPixbuf(buf); return pic; }*/ void gMainWindow::setIcon(gPicture *pic) { gPicture::assign(&_icon, pic); if (!isTopLevel()) return; gtk_window_set_icon(GTK_WINDOW(border), pic ? pic->getPixbuf() : NULL); } bool gMainWindow::topOnly() { if (!isTopLevel()) return false; return top_only; } void gMainWindow::setTopOnly(bool vl) { if (!isTopLevel()) return; gtk_window_set_keep_above (GTK_WINDOW(border),vl); top_only=vl; } void gMainWindow::setMask(bool vl) { if (_mask == vl) return; _mask = vl; drawMask(); } void gMainWindow::setPicture(gPicture *pic) { gPicture::assign(&_picture, pic); drawMask(); } void gMainWindow::remap() { if (!isVisible()) return; gtk_widget_unmap(border); gtk_widget_map(border); if (_skip_taskbar) { setSkipTaskBar(false); setSkipTaskBar(true); } if (top_only) { setTopOnly(false); setTopOnly(true); } if (sticky) { setSticky(false); setSticky(true); } if (stack) { setStacking(0); setStacking(stack); } } void gMainWindow::drawMask() { GdkBitmap *mask; GdkPixmap *back; bool do_remap = false; /*if (win_style) { g_object_unref(win_style); win_style = NULL; }*/ /*if (_picture) { back = _picture->getPixmap(); g_object_ref(back); win_style = gtk_style_copy(widget->style); win_style->bg_pixmap[GTK_STATE_NORMAL] = back; gtk_widget_set_style(widget, win_style); //gdk_window_invalidate_rect(border->window,NULL,true); } else { gtk_widget_set_style(widget, NULL); }*/ if (!isVisible()) return; //if (_background) // g_object_unref(_background); mask = (_mask && _picture) ? _picture->getMask() : NULL; do_remap = !mask && _masked; #ifdef GDK_WINDOWING_X11 XShapeCombineMask(GDK_WINDOW_XDISPLAY(border->window), GDK_WINDOW_XID(border->window), ShapeBounding, 0, 0, mask ? GDK_PIXMAP_XID(mask) : None, ShapeSet); #else //gdk_window_shape_combine_mask(border->window, mask, 0, 0); #endif back = _picture ? _picture->getPixmap() : NULL; gtk_widget_set_double_buffered(border, back == NULL); gtk_widget_set_double_buffered(widget, back == NULL); if (back) { gtk_widget_realize(border); gtk_widget_realize(widget); gdk_window_set_back_pixmap(border->window, back, FALSE); gdk_window_set_back_pixmap(GTK_LAYOUT(widget)->bin_window, back, FALSE); gdk_window_clear(border->window); gdk_window_clear(GTK_LAYOUT(widget)->bin_window); gtk_widget_set_app_paintable(border, true); gtk_widget_set_app_paintable(widget, true); } else { setRealBackground(background()); } if (_background) g_object_unref(_background); _background = back; if (_background) g_object_ref(_background); _masked = mask != NULL; if (do_remap) remap(); else { if (!_skip_taskbar) { setSkipTaskBar(true); setSkipTaskBar(false); } } //gtk_widget_queue_draw(border); #if 0 GdkBitmap *map=NULL; GdkPixmap *pix=NULL; GdkColor black; gint pw,ph; GdkPixbuf *buf_mask = _picture ? _picture->getPixbuf() : NULL; if (win_style) { g_object_unref(win_style); win_style=NULL; } if (buf_mask) { pw=gdk_pixbuf_get_width(buf_mask); ph=gdk_pixbuf_get_height(buf_mask); map=gdk_pixmap_new(NULL,pw,ph,1); gdk_pixbuf_render_threshold_alpha(buf_mask,map,0,0,0,0,pw,ph,128); pix=gdk_pixmap_new(border->window,pw,ph,-1); GdkGC* gc=gdk_gc_new(border->window); fill_gdk_color(&black,0); gdk_gc_set_foreground (gc,&black); gdk_gc_set_background (gc,&black); gdk_draw_rectangle(pix,gc,true,0,0,pw,ph); gdk_draw_pixbuf(pix,gc,buf_mask,0,0,0,0,pw,ph,GDK_RGB_DITHER_MAX,0,0); g_object_unref(G_OBJECT(gc)); } if (buf_mask && _mask) gtk_widget_shape_combine_mask(border,map,0,0); else gtk_widget_shape_combine_mask(border,NULL,0,0); if (map) { win_style=gtk_style_copy(widget->style); win_style->bg_pixmap[GTK_STATE_NORMAL]=pix; gtk_widget_set_style(widget,win_style); gdk_window_invalidate_rect( border->window,NULL,true); g_object_unref(G_OBJECT(map)); } else { gtk_widget_set_style(widget,NULL); if (border->window) //GTK_WIDGET_REALIZED(widget)) { gtk_widget_reset_shapes(border); gdk_window_invalidate_rect(border->window,NULL,true); } } #endif } int gMainWindow::menuCount() { if (!menuBar) return 0; return gMenu::winChildCount(this); } void gMainWindow::setPersistent(bool vl) { persistent = vl; } bool gMainWindow::doClose() { if (_closing) return false; if (opened) { _closing = true; if (onClose) { if (!onClose(this)) opened = false; } else opened = false; _closing = false; if (modal()) gApplication::exitLoop(); } if (!opened) // && !modal()) { if (!modal()) { if (persistent) hide(); else destroy(); } return false; } else return opened; } bool gMainWindow::close() { return doClose(); } int gMainWindow::controlCount() { GList *list = gControl::controlList(); gControl *ctrl; int n = 0; while (list) { ctrl = (gControl *)list->data; if (ctrl->window() == this) n++; list = g_list_next(list); } return n; } gControl *gMainWindow::getControl(char *name) { GList *list = gControl::controlList(); gControl *ctrl; while (list) { ctrl = (gControl *)list->data; if (ctrl->window() == this && !strcasecmp(ctrl->name(), name)) return ctrl; list = g_list_next(list); } return NULL; } gControl *gMainWindow::getControl(int index) { GList *list = gControl::controlList(); gControl *ctrl; int i = 0; while (list) { ctrl = (gControl *)list->data; if (ctrl->window() == this) { if (i == index) return ctrl; i++; } list = g_list_next(list); } return NULL; } int gMainWindow::clientX() { return 0; } int gMainWindow::clientY() { GtkRequisition req; if (menuBar && GTK_WIDGET_VISIBLE(GTK_WIDGET(menuBar))) { gtk_widget_size_request(GTK_WIDGET(menuBar), &req); return req.height; } return 0; } int gMainWindow::clientWidth() { return width(); } int gMainWindow::clientHeight() { GtkRequisition req; int h = 0; if (menuBar && GTK_WIDGET_VISIBLE(GTK_WIDGET(menuBar))) { gtk_widget_size_request(GTK_WIDGET(menuBar), &req); h = req.height; } return height() - h; } void gMainWindow::setActiveWindow(gControl *control) { gMainWindow *window = control ? control->window() : NULL; gMainWindow *old = _active; if (window == _active) return; _active = window; if (old) old->emit(SIGNAL(old->onDeactivate)); if (window) window->emit(SIGNAL(window->onActivate)); } void gMainWindow::setToolBox(bool vl) { _toolbox = vl; // Should be replaced by a "Type" property that tells which is the type of the window // see gdk_window_set_type_hint() stub("gMainWindow::setToolBox"); } void gMainWindow::reparent(gContainer *newpr, int x, int y) { GtkWidget *new_border; int w, h; gColor fg, bg; if (_xembed) return; bg = background(); fg = foreground(); if (isTopLevel() && newpr) { gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); new_border = gtk_event_box_new(); gtk_widget_reparent(widget, new_border); gMenu::embedMenuBar(this, new_border); _no_delete = true; gtk_widget_destroy(border); _no_delete = false; border = new_border; setParent(newpr); connectParent(); borderSignals(); initWindow(); setBackground(bg); setForeground(fg); setFont(font()); gMenu::checkMenuBar(this); move(x, y); gtk_widget_set_size_request(border, width(), height()); } else if (!isTopLevel() && !newpr) { gtk_window_remove_accel_group(GTK_WINDOW(topLevel()->border), accel); // TODO: test that new_border = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_reparent(widget, new_border); gMenu::embedMenuBar(this, new_border); _no_delete = true; gtk_widget_destroy(border); _no_delete = false; border = new_border; parent()->remove(this); parent()->arrange(); setParent(NULL); initWindow(); borderSignals(); setBackground(bg); setForeground(fg); setFont(font()); move(x, y); w = width(); h = height(); bufW = bufH = -1; resize(w, h); } else gControl::reparent(newpr, x, y); }