/*
 * focus_mgr.cc
 *
 * Copyright (C) 1995, 1996, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Kenichi Kourai
 * Copyright (C) 1999, 2000, 2001, 2002 Luiz Blanes
 *
 * 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 blwm; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "main.h"
#include "util.h"
#include "blwm.h"
#include "focus_mgr.h"
#include "blwmrc.h"
#include "key.h"
#include "pager.h"
#include "mini.h"
#include "paging.h"
#include "fbutton.h"
#include "desktop.h"

void FocusMgr::SetFocus(Blwm* blWm)
{
  ASSERT(blWm);

  Blwm::focusBlwm = blWm;

  if (blWm == rootBlwm) {
    desktop.SetFocus();
    scKey->GrabKeys(root);
    blWm->InstallWindowColormaps();

//    DumpStack();

    return;
  }

  if (blWm->CheckMapped()) {
    /*
     * Ungrab buttons so that the window without focus can get focus by
     * ButtonPress.
     */
    if (AutoRaise && blWm == Blwm::activeBlwm)
      for (int i = 1; i < 4; i++)
	XUngrabButton(display, i, 0, blWm->GetWin());
  
    /*
     * Set the input focus to this window.
     */
    XSetInputFocus(display, blWm->GetWin(), RevertToParent, CurrentTime);

    blWm->InstallWindowColormaps();

    /*
     * Redraw according to focus change
     */
    blWm->DrawTitle(True);
    blWm->ChangeFrameFocus();
    for (int i = 0; i < 3; i++)
      blWm->fButton[i]->DrawButton();
  }

  ASSERT(blWm->tButton);
  blWm->tButton->SetFocus();
  blWm->tButton->SetState(Button::PUSH);
  blWm->tButton->DrawButton();

  if (UsePager) {
    ASSERT(blWm->mini);
    blWm->mini->SetFocus();
  }

  // move blWm to the top of the focus stack
  // XXX even if a transient window, give it a focus in the above
  if (!blWm->CheckFlags(TRANSIENT)) {
    Remove(blWm);
    Push(blWm);
  }

  if (blWm->CheckFlags(WM_TAKE_FOCUS))
    blWm->SendMessage(_XA_WM_TAKE_FOCUS);

//  DumpStack();
}

void FocusMgr::ResetFocus(Blwm* blWm)
{
  ASSERT(blWm);

  Blwm::focusBlwm = NULL;

  if (blWm == rootBlwm) {
    scKey->UngrabKeys(root);
    return;
  }

  if (blWm->CheckMapped()) {
    /*
     * Grab buttons so that the window without focus can get focus by
     * ButtonPress.
     */
    for (int i = 1; i < 4; i++)
      XGrabButton(display, i, 0, blWm->GetWin(), True, ButtonPressMask,
		  GrabModeSync, GrabModeAsync, None, cursor[SYS]);
  }

  /*
   * Redraw according to focus change
   */
  blWm->DrawTitle(True);
  blWm->ChangeFrameFocus();
  for (int i = 0; i < 3; i++)
    blWm->fButton[i]->DrawButton();

  ASSERT(blWm->tButton);
  blWm->tButton->ResetFocus();
  blWm->tButton->SetState(Button::NORMAL);
  blWm->tButton->DrawButton();

  if (UsePager) {
    ASSERT(blWm->mini);
    blWm->mini->ResetFocus();
  }
}

/*
 * RollFocus --
 *   Roll focus stack: that is, the top becomes the bottom, and the second
 *   is the top.
 */
void FocusMgr::RollFocus(Bool forward)
{
  Blwm* blWm;

  if (forward) {
    // move this window to the last of the focus list
    blWm = focusMgr.Pop();
    focusMgr.InsertBottom(blWm);
    blWm = focusMgr.Top();
  }
  else {
    // move the last of the focus list to the top of the stack
    blWm = focusMgr.RemoveBottom();
    focusMgr.Push(blWm);
  }

  ASSERT(blWm->CheckMapped());

  blWm->SetFocus();
  blWm->RaiseWindow(True);
  blWm->AdjustPage();
}

/*
 * Roll focus stack within the same screen, not including windows with
 * NOFOCUS or STICKY.
 */
void FocusMgr::RollFocusWithinScreen(Bool forward)
{
  Blwm* blWm;
  List<Blwm>::Iterator i(&focusMgr.GetMapList());
  Rect vt(paging->origin.x, paging->origin.y, rcScreen.width, rcScreen.height);

  if (forward) {
    // move the top window to the bottom if it has a focus
    if (Blwm::focusBlwm != rootBlwm && !Blwm::focusBlwm->CheckFlags(STICKY) &&
	Intersect(Blwm::focusBlwm->GetRect(), vt)) {
      blWm = focusMgr.Pop();
      focusMgr.InsertBottom(blWm);
    }
      
    // search the next window within the same screen
    for (blWm = i.GetHead(); blWm; blWm = i.GetNext()) {
      if (!blWm->CheckFlags(STICKY) && Intersect(blWm->GetRect(), vt)) {
	i.Remove();
	focusMgr.Push(blWm);
	break;
      }
    }
  }
  else {
    // search the next window within the same screen
    for (blWm = i.GetTail(); blWm; blWm = i.GetPrev()) {
      if (!blWm->CheckFlags(STICKY) && Intersect(blWm->GetRect(), vt)) {
	i.Remove();
	focusMgr.Push(blWm);
	break;
      }
    }
  }

  if (blWm == NULL)
    blWm = rootBlwm;

  ASSERT(blWm->CheckMapped());

  blWm->SetFocus();
  blWm->RaiseWindow(True);
}

//////////////////////////////////////////////////////////////////////
// debug function
void FocusMgr::DumpStack()
{
  List<Blwm>::Iterator i(&mapList);

  printf("FOCUS: %s\n", Blwm::focusBlwm->GetName());

  printf("MAP LIST: ");
  for (Blwm* blWm = i.GetHead(); blWm; blWm = i.GetNext())
    printf("%s -> ", blWm->GetName());
  printf("\n");

  i = List<Blwm>::Iterator(&unmapList);

  printf("UNMAP LIST: ");
  for (Blwm* blWm = i.GetHead(); blWm; blWm = i.GetNext())
    printf("%s -> ", blWm->GetName());
  printf("\n");

  printf("\n");
}

Bool FocusMgr::Check(Blwm* blWm)
{
  List<Blwm>::Iterator i(&mapList);

  for (Blwm* tmpBlwm = i.GetHead(); tmpBlwm; tmpBlwm = i.GetNext())
    if (tmpBlwm == blWm)
      return False;
  
  return True;
}


syntax highlighted by Code2HTML, v. 0.9.1