/* awindow.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2026 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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 2 of the License, 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "awindow.h"
#include "text.h"
#include "button.h"
#include <list>
#include <algorithm>
#include "utf8.hh"

AWindow::AWindow( AGUIX *aguix,
		  int x,
		  int y,
		  int width,
		  int height,
		  std::string title,
                  wm_window_type_t type ) : Widget( aguix )
{
  setIsTopLevelWidget( true );

  _x = x;
  _y = y;
  _w = ( width > 0 ) ? width : 1;
  _h = ( height > 0 ) ? height : 1;

  m_open_pos_x = 0;
  m_open_pos_y = 0;
  m_open_max_h = false;
  m_open_max_v = false;
  m_force_open_position = false;

  _bg = _aguix->getFaces().getColor( "default-bg" );

  if ( _bg < 0 ) {
      _bg = 0;
  }

  _title = title;
  onScreen=false;
  border = 5;
  win = 0;
  doTabCycling = false;
  forbidinput = 0;
#ifdef USE_XIM
  inputcontext = NULL;
#endif
  req = NULL;
  mapNr = 0;
  container = NULL;
  contAutoResize = false;
  msgW = msgH = -1;

  _callback_forbidden = false;

  m_window_type = type;

  m_size_hints = NULL;
}

AGUIX *AWindow::getAGUIX() const
{
  return _aguix;
}

void AWindow::sizeChanged(int width,int height, bool explicit_resize)
{
  if ( width != _w || height != _h ) {
    AGMessage *agmsg = AGUIX_allocAGMessage();
    agmsg->type=AG_SIZECHANGED;
    agmsg->size.window=win;
    agmsg->size.neww=width;
    agmsg->size.newh=height;
    agmsg->size.explicit_resize = explicit_resize;
    _aguix->putAGMsg(agmsg);
    _w = width;
    _h = height;
    if ( ( container != NULL ) && ( contAutoResize == true ) ) {
      container->resize( _w, _h );
      container->rearrange();
    }
  }
}

void AWindow::redraw()
{
  if ( _created == true ) {
    for ( childs_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      (*it1)->redraw();
    }
  }
}

Window AWindow::getWindow() const
{
  return win;
}

AWindow::~AWindow()
{
  if ( container != NULL ) {
    delete container;
    container = NULL;
  }
  while ( _childs.size() ) {
    delete *( _childs.begin() );
  }
  if ( req != NULL ) {
    delete req;
    req = NULL;
  }
  destroy();
}

int AWindow::getBG() const
{
  return _bg;
}

void AWindow::setBG(int tbg)
{
  _bg = tbg;
}

void AWindow::ReactMessage(Message *msg)
{
  if((msg->window)!=win) return;
  // diese Message interressiert uns
}

void AWindow::setMaxSize(int mwidth,int mheight)
{
    Display *dsp = _aguix->getDisplay();

    if ( ! m_size_hints ) {
        m_size_hints = XAllocSizeHints();
    }

    if ( m_size_hints ) {
        if ( minw >= 0 ) {
            m_size_hints->flags |= PMaxSize | PMinSize;
            m_size_hints->min_width = minw;
            m_size_hints->min_height = minh;
        } else {
            m_size_hints->flags |= PMaxSize;
        }
        m_size_hints->max_width = mwidth;
        m_size_hints->max_height = mheight;

        XSetWMNormalHints( dsp, win, m_size_hints );
    }

    maxw = mwidth;
    maxh = mheight;
}

void AWindow::setMinSize(int mwidth,int mheight)
{
    Display *dsp = _aguix->getDisplay();

    if ( ! m_size_hints ) {
        m_size_hints = XAllocSizeHints();
    }

    if ( m_size_hints ) {
        if ( maxw >= 0 ) {
            m_size_hints->flags |= PMaxSize | PMinSize;
            m_size_hints->max_width = maxw;
            m_size_hints->max_height = maxh;
        } else {
            m_size_hints->flags |= PMinSize;
        }
        m_size_hints->min_width = mwidth;
        m_size_hints->min_height = mheight;

        XSetWMNormalHints( dsp, win, m_size_hints );
    }

    minw = mwidth;
    minh = mheight;
}

void AWindow::show()
{
  if(onScreen==true) return;
  Display *dsp=_aguix->getDisplay();

  XMapWindow(dsp,win);

  if ( m_force_open_position &&
       ( m_open_max_h || m_open_max_v ) ) {
      // see
      // https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html#idm140130317598336
      XEvent ev;

      memset( &ev, 0, sizeof( ev ) );
      ev.type = ClientMessage;
      ev.xclient.window = win;
      ev.xclient.message_type = _aguix->getAtom( AGUIX::NET_WM_STATE );
      ev.xclient.format = 32;
      ev.xclient.data.l[0] = 1;

      if ( m_open_max_h && m_open_max_v ) {
          ev.xclient.data.l[1] = _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_HORZ );
          ev.xclient.data.l[2] = _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_VERT );
      } else if ( m_open_max_h ) {
          ev.xclient.data.l[1] = _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_HORZ );
          ev.xclient.data.l[2] = 0;
      } else {
          ev.xclient.data.l[1] = _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_VERT );
          ev.xclient.data.l[2] = 0;
      }

      ev.xclient.data.l[3] = 1;
      XSendEvent( dsp, DefaultRootWindow( _aguix->getDisplay() ),
                  False, SubstructureRedirectMask,
                  &ev);
  }
  
  onScreen=true;
  _aguix->xSync();
}

void AWindow::hide()
{
  if(onScreen==false) return;
  Display *dsp=_aguix->getDisplay();
  XUnmapWindow(dsp,win);
  onScreen=false;
}

void AWindow::hide(Window subwin)
{
  if(hasSubWin(subwin)==true) {
    Display *dsp=_aguix->getDisplay();
    XUnmapWindow(dsp,subwin);
  }
}

void AWindow::show(Window subwin)
{
  if(hasSubWin(subwin)==true) {
    Display *dsp=_aguix->getDisplay();
    XMapWindow(dsp,subwin);
  }
}

const char *AWindow::getTitle() const
{
  return _title.c_str();
}

void AWindow::setTitle( std::string newtitle)
{
  _title = newtitle;
  setWMTitle();
}

void AWindow::resizeSubWin(Window twin,int tw,int th)
{
  Display *dsp=_aguix->getDisplay();

  if ( tw < 1 ) tw = 1;
  if ( th < 1 ) th = 1;

  XResizeWindow(dsp,twin,tw,th);
}

void AWindow::moveSubWin(Window twin,int tx,int ty)
{
  Display *dsp=_aguix->getDisplay();
  XMoveWindow(dsp,twin,tx,ty);
}

bool AWindow::handleMessage(XEvent *E,Message *msg)
{
  bool returnvalue=false;
  int res;
  bool conLock;
  GUIElement *guielem;

  // continue when we own lockElement
  conLock = false;
  if ( msg->lockElement != NULL ) {
    if ( contains( msg->lockElement ) == true ) conLock = true;
  }
  
  //check for some input unrelated events
  if ( ( ( msg->type == MapNotify ) &&
         ( msg->window == win ) ) ||
       ( ( msg->type == UnmapNotify ) &&
         ( msg->window == win ) ) ) {
    mapNr++;
  }

  // return if we don't handle messages
  if ( ( getForbidInput() == true ) &&
       ( msg->type != Expose ) &&
       ( msg->type != ConfigureNotify ) &&
       ( msg->type != FocusIn ) &&
       ( msg->type != FocusOut ) ) {
    // but only without lockElement or we don't own it
      if ( conLock == false ) {
          // pretend we handled this event
          if ( isParent( msg->window, false ) == true ) {
              if ( msg->type == KeyRelease ||
                   msg->type == KeyPress ||
                   msg->type == ButtonPress ||
                   msg->type == ButtonRelease ) {
                  return true;
              }
          }
          return returnvalue;
      }
  }

  if ( msg->loop == 0 ) {
    // the following code has to be executed no matter
    // in which state the gui is so does it always but only the first loop
    switch ( msg->type ) {
      case FocusIn:
#ifdef USE_XIM
	if ( inputcontext != NULL ) {
	  if ( msg->window == win ) {
	    XSetICFocus( inputcontext );
	  }
	}
#endif      
	if ( ( getForbidInput() == true ) && ( conLock == false ) ) return returnvalue;
	break;
        case FocusOut:
#ifdef USE_XIM
            if ( inputcontext != NULL ) {
                if ( msg->window == win ) {
                    XUnsetICFocus( inputcontext );
                }
            }
#endif
            _aguix->closeBubbleHelp( this );

            if ( ( getForbidInput() == true ) && ( conLock == false ) ) return returnvalue;
            break;
    }
    
    if(msg->window==win) {
      if(msg->type==ConfigureNotify) {
        if ( ( msg->width != msgW ) || ( msg->height != msgH ) ) {
          msgW = msg->width;
          msgH = msg->height;
          sizeChanged( msg->width, msg->height, false );
	}
      }
    }
  }

  if ( msg->type == ConfigureNotify ) {
      if ( getForbidInput() == true && conLock == false ) return returnvalue;
  }
  
  // Alle Element bekommen Nachricht
  res = 0;
  for ( childs_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
    //TODO Original wurden nur den GUIElement eine msg geschickt,
    //     man koennte aber auch den AWindows die Msg weitergeben
    //     und in AGUIX nur noch Toplevel anstossen
    //     Es koennte aber Probleme machen, wenn ich bei
    //     res == 1 abbreche
    guielem = dynamic_cast<GUIElement*>( (*it1) );
    if ( guielem != NULL ) {
      res = guielem->handleMessageLock( E, msg );
      if ( ( res == 1 ) && ( conLock == false ) ) {
	returnvalue = true;
	break;
      }
    }
  }

  if ( msg->lockElement == NULL ) {
    if ( doTabCycling == true ) {
      if ( isParent( msg->window, false ) == true ) {
	if ( msg->type == KeyPress ) {
	  int keystate = KEYSTATEMASK( msg->keystate );
	  if ( ( msg->key == XK_Tab ) && ( keystate == 0 ) ) {
	    nextFocus();
	  } else if ( ( ( msg->key == XK_ISO_Left_Tab ) ||
			( msg->key == XK_Tab ) ) && ( keystate == ShiftMask ) ) {
	    // my system sends ISO_Left_Tab when tab is pressed together with ShiftMask
	    // so I allow normal tab key and this variant
	    prevFocus();
	  }
	}
      }
    }
  }

  if ( msg->lockElement == NULL &&
       msg->type == KeyPress &&
       isTopLevel() == true &&
       callBackForbidden() == false ) {
    AGMessage *agmsg = AGUIX_allocAGMessage( msg );

    _aguix->executeAfterMsgHandler( [ this, agmsg ] {
        for ( auto &l : key_listener ) {
            //TODO pruefen, ob subwin ue1berhautp ein subwin von this is, also recursiv alle childs pruefen
            if ( ( l.mode & KEYCB_WHENVISIBLE ) == 0 || l.subwin->isVisible() == true ) {
                forbidCallBacks();
                l.cb->run( l.subwin, *agmsg );
                permitCallBacks();
            }
        }
        AGUIX_freeAGMessage( agmsg );
    });
  }

  return returnvalue;
}

bool AWindow::hasSubWin(Window twin) const
{
  std::list<Window>::const_iterator it1;

  it1 = std::find( subwins.begin(), subwins.end(), twin );
  if ( it1 == subwins.end() ) return false;
  return true;
}

Window AWindow::getSubWindow(Window parent,int tx,int ty,int tw,int th)
{
  Window newwin,pwin;
  Display *dsp=_aguix->getDisplay();
  int scr=_aguix->getScreen();
  Visual *vis=DefaultVisual(dsp,scr);
  unsigned long mask=CWEventMask|CWBackPixel|CWColormap;

  if ( tw < 1 ) tw = 1;
  if ( th < 1 ) th = 1;
  
  XSetWindowAttributes attr;
  attr.event_mask=ExposureMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|ButtonMotionMask|EnterWindowMask|LeaveWindowMask|PointerMotionHintMask;
  attr.background_pixel=_aguix->getPixel(_bg);
  attr.colormap=_aguix->getColormap();

  if ( ( parent != 0 ) && ( hasSubWin( parent ) == true ) )
    pwin = parent;
  else
    pwin = win;
  
  newwin=XCreateWindow(dsp,pwin,tx,ty,tw,th,0,_aguix->getDepth(),InputOutput,vis,mask,&attr);
  if(!newwin) {
    return newwin;
  }
  XMapRaised(dsp,newwin);
  XClearWindow(dsp,newwin);
  subwins.push_back( newwin );
  return newwin;
}

void AWindow::removeSubWin(Window subwin)
{
  if(hasSubWin(subwin)==true) {
    Display *dsp=_aguix->getDisplay();
    XDestroyWindow(dsp,subwin);
    subwins.remove( subwin );
  }
}

bool AWindow::isParent(Window childwin,bool direct) const
{
  bool found = false;
  AWindow *twin;
  
  if ( direct == false ) {
    for ( childs_const_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      twin = dynamic_cast<AWindow*>( (*it1) );
      if ( twin != NULL ) {
          if ( twin->isParent( childwin, false ) == true ) {
              found = true;
              break;
          }
      }
    }
  }
  if ( found == false && hasSubWin( childwin ) == true ) found = true;
  if ( childwin == win ) found = true;
  return found;
}

bool AWindow::isParent( Window childwin ) const
{
  return isParent( childwin, true );
}

void AWindow::move(int nx,int ny)
{
  Display *dsp=_aguix->getDisplay();
  XMoveWindow(dsp,win,nx,ny);
  _x = nx;
  _y = ny;
}

void AWindow::resize(int nw,int nh)
{
  int newminw, newminh, newmaxw, newmaxh;

  if ( ( nw > 0 ) && ( nh > 0 ) ) {
    if ( ( nw != _w ) || ( nh != _h ) ) {
      Display *dsp=_aguix->getDisplay();

      // first check if we doesn't crash with the min/maxwidths
      // some WMs doesn't allow resize in this case (atleast KWM)
      if ( minw > 0 ) {
	newminw = ( nw < minw ) ? nw : minw;
	newminh = ( nh < minh ) ? nh : minh;
	if ( ( newminw != minw ) || ( newminh != minh ) ) {
	  setMinSize( newminw, newminh );
	}
      }
      if ( maxw > 0 ) {
	newmaxw = ( nw > maxw ) ? nw : maxw;
	newmaxh = ( nh > maxh ) ? nh : maxh;
	if ( ( newmaxw != maxw ) || ( newmaxh != maxh ) ) {
	  setMaxSize( newmaxw, newmaxh );
	}
      }

      XResizeWindow(dsp,win,nw,nh);
#if 0
      // it's possible to not call sizeChanged here
      // but the size has to be stored so if one use
      // this case, the test in sizeChanged for different size
      // has to be removed because it would be always true in the
      // manual resize case
      _w = nw;
      _h = nh;
#else
      sizeChanged( nw, nh, true );
#endif
    }
  }
}

Widget *AWindow::findWidgetForWindow( Window child )
{
  childs_const_it it1;
  
  for ( it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
    if ( (*it1)->isParent( child ) == true ) break;
  }
  if ( it1 == _childs.end() ) return NULL;
  return *it1;
}

void AWindow::setCursor(int type)
{
  _aguix->setCursor(win,type);
}

void AWindow::unsetCursor()
{
  _aguix->unsetCursor(win);
}

void AWindow::invalidFocus()
{
  Focus *f;
  Widget *w;
  
  // we could had a focus owner before getting a parent awindow
  // so first check if we have not one
  if(_parent!=NULL) {
    _parent->invalidFocus();
  }
  // if we have a focus owner, release it
  f = getFocusElement( this );
  if ( f != NULL ) {
    setFocusElement( this, NULL );
    w = dynamic_cast<Widget*>( f );
    if ( w != NULL )
      w->redraw();
  }
}

void AWindow::applyFocus( Widget *newfocus )
{
  if ( ( newfocus != NULL ) && ( isOwner( newfocus ) == true ) ) return;
  
  invalidFocus();
  if ( newfocus != NULL ) {
    if ( _parent != NULL ) {
      _parent->applyFocus( newfocus );
    } else {
      setFocusElement( this, newfocus );
      newfocus->redraw();
    }
  }
}

bool AWindow::isOwner( Widget *f ) const
{
  if(_parent!=NULL) {
    return _parent->isOwner(f);
  } else {
    if ( getFocusElement( this ) == f ) return true;
  }
  return false;
}

void AWindow::maximizeX()
{
  int mw,tw;

  mw = 0;

  for ( childs_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
    tw = (*it1)->getX() + (*it1)->getWidth() + border;
    if ( tw > mw )
      mw = tw;
  }
  if ( mw > _w ) {
    resize( mw, _h );
  }
}

void AWindow::maximizeY()
{
  int mh,th;

  mh = 0;
  for ( childs_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
    th = (*it1)->getY() + (*it1)->getHeight() + border;
    if ( th > mh )
      mh = th;
  }
  if ( mh > _h ) {
    resize( _w, mh );
  }
}

int AWindow::getBorderWidth() const
{
  return border;
}

void AWindow::setBorderWidth( int nv )
{
  if ( ( nv < 0 ) ||
       ( nv >= _w/2 ) ) return;
  border = nv;
}

int AWindow::addTextFromString( const char *text,
				int tx,
				int ty,
				int vspace,
				Text ***return_texts,
				int *return_count,
				int *return_y )
{
  int i;
  int lines;
  char **liness;
  Text **text_elems;

  if ( text == NULL ) return 1;

  if ( tx < 0 ) tx = 0;
  if ( ty < 0 ) ty = 0;
  if ( vspace < 0 ) vspace = 0;

  lines = createLines( text, &liness );

  text_elems = (Text**)_allocsafe( sizeof( Text* ) * lines );

  for ( i = 0; i < lines; i++ ) {
    text_elems[i] = (Text*)add( new Text( _aguix, tx, ty, liness[i] ) );
    ty += text_elems[i]->getHeight() + vspace;
  }

  if ( return_texts != NULL ) {
    // caller want the Text list
    *return_texts = text_elems;
  } else _freesafe( text_elems );

  if ( return_count != NULL ) {
    *return_count = lines;
  }
  
  if ( return_y != NULL ) {
    // ty is not the real y value because vspace is always added
    *return_y = ty - vspace;
  }

  for ( i = 0; i < lines; i++ ) _freesafe( liness[i] );
  _freesafe( liness );

  return 0;
}

int AWindow::addMultiLineText( const std::string text,
                               AContainer &ac,
                               int xpos, int ypos,
                               AContainer **return_container,
                               std::list<Text*> *return_text_list,
                               bool split_on_newline )
{
  int i;
  int lines;
  char **liness;
  Text *textelement;

  if ( split_on_newline ) {
      lines = createLines( text.c_str(), &liness, '\n' );
  } else {
      lines = createLines( text.c_str(), &liness );
  }

  AContainer *textac = ac.add( new AContainer( this, 1, lines ), xpos, ypos );
  textac->setMinSpace( 5 );
  textac->setMaxSpace( 5 );
  textac->setBorderWidth( 0 );

  for ( i = 0; i < lines; i++ ) {
    textelement = (Text*)textac->add( new Text( _aguix, 0, 0, liness[i] ),
                                      0, i, AContainer::CO_INCWNR );
    if ( return_text_list != NULL ) {
      return_text_list->push_back( textelement );
    }
  }

  for ( i = 0; i < lines; i++ ) _freesafe( liness[i] );
  _freesafe( liness );

  if ( return_container != NULL ) *return_container = textac;
  return 0;
}

void AWindow::centerScreen()
{
  int rw, rh, nx, ny;

  _aguix->getLargestDimensionOfCurrentScreen( NULL, NULL, &rw, &rh );

  if ( ( rw < 1 ) || ( rh < 1 ) ) return;
  
  nx = ( rw / 2 ) - ( _w / 2 );
  ny = ( rh / 2 ) - ( _h / 2 );
  move( nx, ny );
}

bool AWindow::contains( Widget *elem ) const
{
  bool returnvalue = false;
  AWindow *twin;

  for ( childs_const_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
    if ( (*it1) == elem ) {
      returnvalue = true;
      break;
    }
  }
  if ( returnvalue == false ) {
    for ( childs_const_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      twin = dynamic_cast<AWindow*>( (*it1) );
      if ( twin != NULL ) {
	if ( twin->contains( elem ) == true ) {
	  returnvalue = true;
	  break;
	}
      }
    }
  }
  return returnvalue;
}

Widget *AWindow::getFocusOwner() const
{
  Focus *f;

  if ( _parent != NULL )
    return _parent->getFocusOwner();
  f = getFocusElement( this );
  if ( f == NULL ) return NULL;
  return dynamic_cast<Widget*>( f );
}

void AWindow::nextFocus()
{
  Widget *tw, *next;
  Focus *f;

  if ( _parent != NULL ) {
    _parent->nextFocus();
  } else {

    f = getFocusElement( this );
    tw = dynamic_cast<Widget*>( f );
    next = searchNextFocus( tw );

    if ( next != NULL ) {
      applyFocus( next );
    }
  }
}

void AWindow::prevFocus()
{
  Widget *tw, *prev;
  Focus *f;

  if ( _parent != NULL ) {
    _parent->prevFocus();
  } else {

    f = getFocusElement( this );
    tw = dynamic_cast<Widget*>( f );
    prev = searchPrevFocus( tw );

    if ( prev != NULL ) {
      applyFocus( prev );
    }
  }
}

void AWindow::setDoTabCycling( bool nv )
{
  doTabCycling = nv;
}

bool AWindow::getDoTabCycling() const
{
  return doTabCycling;
}

bool AWindow::isTopParent( Window childwin )
{
  if ( _parent != NULL )
    return _parent->isTopParent( childwin );
  else
    return isParent( childwin, false );
}

bool AWindow::isVisible() const
{
  if ( onScreen == false ) return false;
  else {
    if ( _parent != NULL ) {
      if ( _parent->isVisible() == true ) return true;
      else return false;
    } else {
      return true;
    }
  }
}

void AWindow::useStippleBackground()
{
  _aguix->setWindowBackgroundPixmap( win );
}

void AWindow::forbidUserInput()
{
  forbidinput++;
}

void AWindow::permitUserInput()
{
  forbidinput--;
  if ( forbidinput < 0 ) forbidinput = 0;
}

bool AWindow::getForbidInput() const
{
  if ( _parent != NULL )
    return _parent->getForbidInput();
  else
    return ( forbidinput == 0 ) ? false : true;
}

void AWindow::setTransientForAWindow( const AWindow *twin )
{
  if ( win == 0 ) return;
  if ( twin == NULL ) {
    XSetTransientForHint( _aguix->getDisplay(), getWindow(), None );
  } else {
    XSetTransientForHint( _aguix->getDisplay(), getWindow(), twin->getWindow() );
  }
}

#ifdef USE_XIM
XIC AWindow::getXIC() const
{
  return inputcontext;
}

int AWindow::createXIC()
{
  long im_event_mask;
  XWindowAttributes attr;

  if ( _parent != NULL ) {
    inputcontext = NULL;
    return 0;
  }

  if ( ( _aguix->getXIM() != NULL ) && ( _aguix->getXIMStyle() != 0 ) ) {
    _aguix->Flush();
    inputcontext = XCreateIC( _aguix->getXIM(),
			      XNInputStyle, _aguix->getXIMStyle(),
			      XNClientWindow, win,
			      (void*)NULL );
    if ( inputcontext == NULL ) {
      fprintf( stderr, "Worker Warning: Cannot create input context\n");
    } else {
      XGetICValues( inputcontext, XNFilterEvents, &im_event_mask, (void*)NULL );
      XGetWindowAttributes( _aguix->getDisplay(), win, &attr );
      XSelectInput( _aguix->getDisplay(), win, attr.your_event_mask | im_event_mask );
    }
  } else {
    inputcontext = NULL;
  }
  return ( ( inputcontext != NULL ) ? 0 : 1 );
}

void AWindow::closeXIC()
{
  if ( inputcontext != NULL ) {
    XDestroyIC( inputcontext );
    inputcontext = NULL;
  }
}

void AWindow::XICdestroyed()
{
  inputcontext = NULL;
}

#endif

bool AWindow::isTopLevel() const
{
  return ( ( _parent == NULL ) ? true : false );
}

int AWindow::request( const char *reqtitle, const char *text, const char *buttons, Requester::request_flags_t flags )
{
  if ( _parent != NULL ) {
    return _parent->request( reqtitle, text, buttons, flags );
  }

  if ( req == NULL ) {
    req = new Requester( _aguix, this );
  }
  return req->request( reqtitle, text, buttons, flags );
}

int AWindow::request( const char *reqtitle,
                      const char *text,
                      const std::vector< std::vector< std::pair< std::string, int > > > &button_rows,
                      Requester::request_flags_t flags )
{
    if ( _parent != NULL ) {
        return _parent->request( reqtitle, text, button_rows, flags );
    }

    if ( req == NULL ) {
        req = new Requester( _aguix, this );
    }
    return req->request( reqtitle, text, button_rows, NULL, flags );
}

int AWindow::string_request( const char *reqtitle,
                             const char *lines,
                             const char *default_str,
                             const char *buttons,
                             char **return_str,
                             Requester::request_flags_t flags )
{
    return string_request( reqtitle, lines, default_str, buttons, return_str,
                           flags, Requester::request_options{} );
}

int AWindow::string_request( const char *reqtitle, const char *lines, const char *default_str, const char *buttons, char **return_str,
                             Requester::request_flags_t flags,
                             const Requester::request_options &options )
{
  if ( _parent != NULL ) {
      return _parent->string_request( reqtitle, lines, default_str, buttons, return_str, flags, options );
  }

  if ( req == NULL ) {
    req = new Requester( _aguix, this );
  }
  return req->string_request( reqtitle, lines, default_str, buttons, return_str, NULL, flags, options );
}

int AWindow::getMapNr() const
{
  return mapNr;
}

AContainer *AWindow::setContainer( AContainer *newcont, bool autoResize )
{
  container = newcont;
  contAutoResize = autoResize;
  return newcont;
}

void AWindow::removeContainer( AContainer *cont )
{
  if ( container == cont ) {
    container = NULL;
  }
}

void AWindow::removeFromContainer( Widget *twid )
{
  if ( container != NULL ) {
    container->remove( twid );
  }
}

void AWindow::contMaximize( bool applyMinSize, bool applyMaxSize, bool grow_only )
{
    int tw, th;

    if ( container == NULL ) {
        return;
    }

    // to maximize the window get the min width/height for
    // the container by resizing to 0,0 and finally add position

    int ow = container->getWidth();
    int oh = container->getHeight();
      
    container->resize( 0, 0 );

    int minw = container->getWidth();
    int minh = container->getHeight();

    if ( ! grow_only ) {
        tw = minw;
        th = minh;
    } else {
        bool resize_again = false;

        if ( minw >= ow ) {
            tw = minw;
        } else {
            tw = ow;
            resize_again = true;
        }
        if ( minh >= oh ) {
            th = minh;
        } else {
            resize_again = true;
            th = oh;
        }

        if ( resize_again ) {
            container->resize( tw, th );
        }
    }

    container->rearrange();

    tw += container->getX();
    minw += container->getX();
    th += container->getY();
    minh += container->getY();
    if ( applyMinSize == true ) setMinSize( minw, minh );
    if ( applyMaxSize == true ) setMaxSize( minw, minh );
    resize( tw, th );
}

void AWindow::doCreateStuff()
{
  Display *dsp = _aguix->getDisplay();
  Window ParentWnd;
  int scr;
  Visual *vis;
  unsigned long mask = CWEventMask | CWBackPixel | CWColormap;
  XSetWindowAttributes attr;
  XWMHints *WMHints;

  scr = _aguix->getScreen();
  vis = DefaultVisual( dsp, scr );

  if ( _parent == NULL ) {
    ParentWnd = RootWindow( dsp, scr );
  } else {
    ParentWnd = _parent->win;
  }

  attr.event_mask =
    ExposureMask |
    ButtonPressMask |
    ButtonReleaseMask |
    StructureNotifyMask |
    KeyPressMask |
    KeyReleaseMask |
    ButtonMotionMask |
    PointerMotionHintMask |
    FocusChangeMask;
  attr.background_pixel = _aguix->getPixel( _bg );
  attr.colormap = _aguix->getColormap();
  
  // create window
  win = XCreateWindow( dsp, ParentWnd,
                       m_force_open_position ? m_open_pos_x : _x,
                       m_force_open_position ? m_open_pos_y : _y,
                       _w, _h, 0, _aguix->getDepth(), InputOutput, vis, mask, &attr );
  if ( !win ) {
    return;
  }

  maxw = -1;
  maxh = -1;
  minw = -1;
  minh = -1;

  // now set advanced window attributes (for window managers...)
  setWMTitle();

  // for close button
  XSetWMProtocols( dsp, win, _aguix->getCloseAtom(), 1 );

  // size of the window
  if ( ! m_size_hints ) {
      m_size_hints = XAllocSizeHints();
  }

  if ( m_size_hints ) {
      m_size_hints->flags = PSize;
      m_size_hints->width = _w;
      m_size_hints->height = _h;

      if ( m_force_open_position ) {
          m_size_hints->flags |= USPosition;

          m_size_hints->x = m_open_pos_x;
          m_size_hints->y = m_open_pos_y;
      }
      
      XSetWMNormalHints( dsp, win, m_size_hints );
  }

  // window grouping for window managers
  WMHints = XAllocWMHints();
  if ( WMHints ) {
      WMHints->input = True;
#if 0
      WMHints->window_group = _aguix->getGroupWin();
      WMHints->flags = InputHint | WindowGroupHint;
#else
      WMHints->flags = InputHint;
#endif
      XSetWMHints( dsp, win, WMHints );
      XFree( WMHints );
  }
  
  // command so the window manager can restart worker
  XSetCommand( dsp, win, _aguix->getargv(), _aguix->getargc() );

  if ( _aguix->getApplyWindowDialogType() ) {
      Atom window_type_atom;

      if ( m_window_type == AWINDOW_DIALOG ) {
          window_type_atom = _aguix->getAtom( AGUIX::NET_WM_WINDOW_TYPE_DIALOG );
      } else {
          window_type_atom = _aguix->getAtom( AGUIX::NET_WM_WINDOW_TYPE_NORMAL );
      }

      XChangeProperty( dsp, win,
                       _aguix->getAtom( AGUIX::NET_WM_WINDOW_TYPE ),
                       XA_ATOM, 32, PropModeReplace, (unsigned char *) &window_type_atom, 1 );
  }

  if ( m_window_type == AWINDOW_DIALOG ) {
      const AWindow *twin = _aguix->getTransientWindow();

      if ( twin ) {
          setTransientForAWindow( twin );
      }
  }

  applyIcon();

  // clear window
  XClearWindow( dsp, win );

  // insert window into _aguix
  if ( m_window_type == AWINDOW_DIALOG ) {
      _aguix->insertWindow( this, false );
  } else {
      _aguix->insertWindow( this, true );
  }
  
#ifdef USE_XIM
  // create xinput stuff
  createXIC();
#endif
  Widget::doCreateStuff();
  
  _aguix->Flush();
  return;
}

void AWindow::doDestroyStuff()
{
  if ( _created == true ) {
    Display *dsp=_aguix->getDisplay();
    _aguix->removeWindow( this );

#ifdef WMDESTROYBUGFIX
    if ( _parent == NULL ) {
      _aguix->Flush();
      XSync( dsp, False );
      waittime( 10 );
    }
#endif
#ifdef USE_XIM
    closeXIC();
#endif    
    if ( win != 0 ) {
      XDestroyWindow( dsp, win );
      win = 0;
    }
    _aguix->Flush();

    // to avoid a possible bug in xfw4
    // (https://gitlab.xfce.org/xfce/xfwm4/-/issues/514) I call XSync
    // here. AWindows are not opened/closed that often so it should be fine.
    _aguix->xSync();

    if ( m_size_hints ) {
        XFree( m_size_hints );
        m_size_hints = NULL;
    }
  }

  onScreen = false;
  _title = "";
  Widget::doDestroyStuff();
}

void AWindow::remove( Widget *w )
{
  std::list<Widget*>::iterator it1;
  Widget *focusw;
  
  if ( w == NULL ) return;
  
  it1 = std::find( _childs.begin(), _childs.end(), w );
  if ( it1 == _childs.end() ) return; // not found
  
  if ( _created == true ) w->destroy();
  focusw = getFocusOwner();
  if ( focusw != NULL ) {
    // check if the child has the focusowner
    if ( w->contains( focusw ) == true ) {
      invalidFocus();
    }
  }
  if ( container != NULL ) removeFromContainer( w );
  if ( _parent != NULL ) _parent->removeFromContainer( w );
  w->setParent( NULL );
  _childs.remove( w );
}

int AWindow::create()
{
  std::list<Widget*>::iterator it1;
  int res;
  
  res = Widget::create();
  if ( _created == true ) {
    for ( it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      (*it1)->create();
    }
  }
  return res;
}

void AWindow::destroy()
{
  std::list<Widget*>::iterator it1;

  if ( _parent != NULL ) {
    _parent->removeKeyCallBack( this, NULL );
  }

  if ( _created == true ) {
    for ( it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      (*it1)->destroy();
    }
  }
  Widget::destroy();
}

int AWindow::setParent( class AWindow *parent )
{
  // do not accept parent if already created
  //TODO it would be possible to destroy the window in
  // this case and reopen it
  if ( _created == true ) return 1;
  //TODO Wenn das Fenster schon ein Fokuselement haelt,
  //     Dann sollte es dem parent uebergeben werden
  return Widget::setParent( parent );
}

int AWindow::setWMTitle()
{
  Display *dsp;
  XClassHint classhint;
  XTextProperty windowname;
  char *tstr;
  
  dsp = _aguix->getDisplay();
  if ( ( dsp == NULL ) || ( win == 0 ) ) return 1;
  
  classhint.res_name = dupstring( _title.c_str() );
  classhint.res_class = dupstring( _aguix->getClassname().c_str() );
  XSetClassHint( dsp, win, &classhint );
  _freesafe( classhint.res_class );
  _freesafe( classhint.res_name );
  
  tstr = dupstring( _title.c_str() );

  if ( XStringListToTextProperty( &tstr, 1, &windowname ) != 0 ) {
      XTextProperty ticonname;
      if ( XStringListToTextProperty( &tstr, 1, &ticonname ) != 0 ) {
          XSetWMName( dsp, win, &windowname );
          XSetWMIconName( dsp, win, &ticonname );
          XFree( ticonname.value );
      }
      XFree( windowname.value );
  }

#if defined( X_HAVE_UTF8_STRING ) && !defined( DISABLE_UTF8_SUPPORT )
  if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED && UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
      XTextProperty utf8name;

      if ( XmbTextListToTextProperty( dsp, &tstr, 1, XUTF8StringStyle, &utf8name ) == Success ) {
          XSetTextProperty( dsp, win, &utf8name, _aguix->getAtom( AGUIX::NET_WM_NAME_ATOM ) );
          XSetTextProperty( dsp, win, &utf8name, _aguix->getAtom( AGUIX::NET_WM_ICON_NAME_ATOM ) );
          XFree( utf8name.value );
      }
  }
#endif

  _freesafe( tstr );
  
  XStoreName( dsp, win, _title.c_str() );
  
  return 0;
}

const std::list<Widget*> &AWindow::getChilds() const
{
  return _childs;
}

int AWindow::searchNextFocus( focus_search_mode_t &search_mode,
			      const Widget *element,
			      Widget **new_element,
			      Widget **prev_element )
{
  AWindow *twin;
  
  if ( search_mode == SEARCH_FINISHED ) {
    return 0;
  } else if ( ( search_mode == SEARCH_NEXT ) && ( getAcceptFocus() == true ) && ( isVisible() == true ) ) {
    *new_element = this;
    search_mode = SEARCH_FINISHED;
  } else {
    if ( ( search_mode == SEARCH_WIDGET ) && ( this == element ) ) search_mode = SEARCH_NEXT;
    else if ( ( search_mode == SEARCH_WIDGET ) &&
	      ( this != element ) &&
	      ( prev_element != NULL ) &&
	      ( getAcceptFocus() == true ) &&
	      ( isVisible() == true ) )
      *prev_element = this;
    
    for ( childs_it it1 = _childs.begin(); it1 != _childs.end(); it1++ ) {
      twin = dynamic_cast<AWindow*>( (*it1) );
      if ( twin != NULL ) {
	twin->searchNextFocus( search_mode, element, new_element, prev_element );
      } else {
	if ( ( search_mode == SEARCH_WIDGET ) && ( (*it1) == element ) ) search_mode = SEARCH_NEXT;
	else if ( ( search_mode == SEARCH_WIDGET ) &&
		  ( (*it1) != element ) &&
		  ( prev_element != NULL ) &&
		  ( (*it1)->getAcceptFocus() == true ) &&
		  ( (*it1)->isVisible() == true ) ) *prev_element = (*it1);
	else if ( ( search_mode == SEARCH_NEXT ) && ( (*it1)->getAcceptFocus() == true ) && ( (*it1)->isVisible() == true ) ) {
	  *new_element = (*it1);
	  search_mode = SEARCH_FINISHED;
	}
      }
      if ( search_mode == SEARCH_FINISHED ) break;
    }
  }
  return 0;
}

Widget *AWindow::searchNextFocus( const Widget *current )
{
  focus_search_mode_t mode = SEARCH_WIDGET;
  Widget *new_element = NULL;

  if ( current == NULL ) mode = SEARCH_NEXT;
  searchNextFocus( mode, current, &new_element );

  if ( new_element == NULL ) {
    // no new focus element found
    // start again with mode NEXT
    mode = SEARCH_NEXT;
    searchNextFocus( mode, current, &new_element );
  }
  
  // return element
  // it it is NULL it just means there is no focus element
  return new_element;
}

Widget *AWindow::searchPrevFocus( const Widget *current )
{
  focus_search_mode_t mode = SEARCH_WIDGET;
  Widget *new_element = NULL, *prev_element = NULL;

  if ( current == NULL ) mode = SEARCH_NEXT;
  searchNextFocus( mode, current, &new_element, &prev_element );

  if ( prev_element == NULL ) {
    // no prev found so start again with NULL element which
    // will go through all elements and should return the last
    // which is the only one which can not be found by the
    // first run
    mode = SEARCH_WIDGET;
    searchNextFocus( mode, NULL, &new_element, &prev_element );
  }

  // return element
  // it it is NULL it just means there is no focus element
  return prev_element;
}

int AWindow::addWindowEvent( Window subwin, long mask )
{
  XWindowAttributes curattr;
 
  if ( hasSubWin( subwin ) == false )
    return 1;

  XGetWindowAttributes( _aguix->getDisplay(), subwin, &curattr );
  XSelectInput( _aguix->getDisplay(), subwin, curattr.your_event_mask | mask );
  return 0;
}

int AWindow::getMinWidth() const
{
  return minw;
}

int AWindow::getMinHeight() const
{
  return minh;
}

int AWindow::getMaxWidth() const
{
  return maxw;
}

int AWindow::getMaxHeight() const
{
  return maxh;
}

void AWindow::addKeyCallBack( CallBack *new_cb, int mode )
{
  addKeyCallBack( this, new_cb, mode );
}

void AWindow::addKeyCallBack( AWindow *subwin, CallBack *new_cb, int mode )
{
  if ( _parent != NULL ) {
    _parent->addKeyCallBack( subwin, new_cb, mode );
  } else {
    key_listener_t k;

    k.cb = new_cb;
    k.subwin = subwin;
    k.mode = mode;
    key_listener.push_back( k );
  }
}

void AWindow::removeKeyCallBack( AWindow *subwin, CallBack *cb )
{
  if ( _parent != NULL ) {
    _parent->removeKeyCallBack( subwin, cb );
  } else {
    std::list<key_listener_t>::iterator it;
    
    for ( it = key_listener.begin(); it != key_listener.end(); it++ ) {
      if ( (*it).subwin == subwin )  {
        if ( cb == NULL || (*it).cb == cb ) {
          it = key_listener.erase( it );
        }
      }
    }
  }
}

void AWindow::removeKeyCallBack( CallBack *cb )
{
  if ( cb == NULL ) return;  // removal  of all callbacks are not allowed from public interface
  
  removeKeyCallBack( this, cb );
}

void AWindow::forbidCallBacks()
{
  _callback_forbidden = true;
  if ( _parent != NULL ) _parent->forbidCallBacks();
}

void AWindow::permitCallBacks()
{
  if ( _parent != NULL ) _parent->permitCallBacks();
  _callback_forbidden = false;
}

bool AWindow::callBackForbidden() const
{
  if ( _callback_forbidden == true ) return true;
  if ( _parent != NULL ) return _parent->callBackForbidden();
  return false;
}

void AWindow::toFront()
{
    if ( _created == false ) return;
    _aguix->WindowtoFront( win );
}

void AWindow::updateCont()
{
    if ( container != NULL ) {
        container->resize( getWidth(), getHeight() );
        container->rearrange();
    }
}

int AWindow::isMaximized()
{
    Atom ret_type;
    int ret_format;
    unsigned long ret_len,ret_after;
    unsigned char *ret_prop;

    int res = 0;
    
    if ( _created == false ) return res;

    if ( XGetWindowProperty( _aguix->getDisplay(),
                             win,
                             _aguix->getAtom( AGUIX::NET_WM_STATE ),
                             0,
                             32,
                             False,
                             AnyPropertyType,
                             &ret_type,
                             &ret_format,
                             &ret_len,
                             &ret_after,
                             &ret_prop ) == Success ) {
        if ( ret_len > 0 &&
             ret_type == XA_ATOM &&
             ret_format == 32 ) {
            const Atom *atoms = (const Atom*)ret_prop;
            for ( unsigned long i = 0; i < ret_len; i++ ) {
                if ( atoms[i] == _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_HORZ ) ) {
                    res |= 1;
                } else if ( atoms[i] == _aguix->getAtom( AGUIX::NET_WM_STATE_MAXIMIZED_VERT ) ) {
                    res |= 2;
                }
            }
        }
        XFree( ret_prop );
    }

    return res;
}

int AWindow::getRootPosition( int *x,
                              int *y )
{
    Window child;

    if ( _created == false ) return 1;

    if ( XTranslateCoordinates( _aguix->getDisplay(),
                                win,
                                DefaultRootWindow( _aguix->getDisplay() ),
                                0, 0,
                                x, y,
                                &child ) == True ) {
        return 0;
    }

    return 1;
}

void AWindow::forceOpenPosition( int x, int y,
                                 bool max_h,
                                 bool max_v )
{
    m_open_pos_x = x;
    m_open_pos_y = y;
    m_open_max_h = max_h;
    m_open_max_v = max_v;
    m_force_open_position = true;
}

void AWindow::enableXDND()
{
    Atom version = 5;

    XChangeProperty( _aguix->getDisplay(),
                     win, _aguix->getAtom( AGUIX::XDNDAWARE ),
                     XA_ATOM, 32,
                     PropModeReplace, (unsigned char*)&version, 1 );

    m_enable_xdnd = true;
}

bool AWindow::acceptXDND()
{
    return m_enable_xdnd;
}

Window AWindow::findWindowForRootPos( int rx, int ry )
{
    int x, y;
    Window child;
    Window previous_child = None;
    Window current_window = win;

    if ( XTranslateCoordinates( _aguix->getDisplay(),
                                DefaultRootWindow( _aguix->getDisplay() ),
                                current_window,
                                rx, ry,
                                &x, &y,
                                &child ) != True ) {
        return None;
    }

    previous_child = child;

    while ( child != None ) {
        previous_child = child;

        if ( XTranslateCoordinates( _aguix->getDisplay(),
                                    current_window,
                                    child,
                                    x, y,
                                    &x, &y,
                                    &child ) != True ) {
            return None;
        }

        current_window = previous_child;
    }

    return previous_child;
}

Widget *AWindow::findWidgetForRootPos( int rx, int ry )
{
    Window child_win = findWindowForRootPos( rx, ry );

    if ( child_win == None ) return NULL;

    Widget *w = findWidgetForWindow( child_win );

    if ( AWindow *awin = dynamic_cast< AWindow * >( w ) ) {
        return awin->findWidgetForWindow( child_win );
    }

    return w;
}

void AWindow::applyIcon()
{
    const unsigned long *data = NULL;
    size_t data_length = 0;

    if ( ! _aguix->getStandardIconData( &data, &data_length ) ) {
        return;
    }

    XChangeProperty( _aguix->getDisplay(),
                     win,
                     _aguix->getAtom( AGUIX::NET_WM_ICON_ATOM ),
                     _aguix->getAtom( AGUIX::CARDINAL_ATOM ),
                     32, PropModeReplace, (const unsigned char*)data, data_length );
}

void AWindow::setOverrideRedirect()
{
    if ( ! _created ) return;

    XSetWindowAttributes set_attr;
    set_attr.override_redirect = True;
    XChangeWindowAttributes( _aguix->getDisplay(), win, CWOverrideRedirect, &set_attr );
}
