/*
 * titlebar.cc
 *
 * Copyright (C) 1995-2001 Kenichi Kourai
 *
 * 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, 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 qvwm; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "main.h"
#include "qvwm.h"
#include "qvwmrc.h"
#include "event.h"
#include "fbutton.h"
#include "tooltip.h"

QvImage* Qvwm::imgTitlebar;
QvImage* Qvwm::imgActiveTitlebar;

unsigned long* gradPattern;
unsigned long* gradActivePattern;

/*
 * CreateTitle --
 *   Create title window, which is the child of frame window.
 */
void Qvwm::CreateTitle(const Rect& rect)
{
  XSetWindowAttributes attributes;
  unsigned long valueMask;

  attributes.event_mask = ButtonPressMask | ButtonReleaseMask | ExposureMask |
                          EnterWindowMask | LeaveWindowMask |
			  Button1MotionMask | PointerMotionMask;
  attributes.background_pixel = darkGray.pixel;
  valueMask = CWBackPixel | CWEventMask;

  title = XCreateWindow(display, frame,
			rect.x, rect.y, rect.width, rect.height,
			0, CopyFromParent, InputOutput, CopyFromParent,
			valueMask, &attributes);
  XSaveContext(display, title, context, (caddr_t)this);

  rcTitle = rect;
  CalcShortName();

  toolTip = new Tooltip();
  toolTip->SetString(name);

  if (GradTitlebar) {
    pixGrad = None;
    pixActiveGrad = None;
  }

  if (TitlebarImage) {
    imgTitle = imgTitlebar->Duplicate();
    imgTitle->SetBackground(title);
  }
  if (TitlebarActiveImage)
    imgActiveTitle = imgActiveTitlebar->Duplicate();
}

char* Qvwm::GetNameFromHint()
{
  XTextProperty xtp;
  char** cl;
  int n;

  delete [] name;

  if (XGetWMName(display, GetWin(), &xtp) != 0) {
    XmbTextPropertyToTextList(display, &xtp, &cl, &n);
    if (cl) {
      name = new char[strlen(cl[0]) + 1];
      strcpy(name, cl[0]);
      XFreeStringList(cl);
    }
    else {
      name = new char[9];
      strcpy(name, "Untitled");
    }
  }
  else {
    name = new char[9];
    strcpy(name, "Untitled");
  }

  return name;
}

void Qvwm::CalcShortName()
{
  delete [] shortname;

  int nameWidth = rcTitle.width - 2;  // 2 is the space between name and button
  int num = 0;

  if (CheckFlags(CTRL_MENU))
    nameWidth -= TaskbarButton::SYMBOL_SIZE + 4;
  else
    nameWidth -= 4;

  if (CheckFlags(BUTTON1))
    num++;
  if (CheckFlags(BUTTON2))
    num++;
  if (CheckFlags(BUTTON3))
    num++;

  nameWidth -= BUTTON_WIDTH * num + 4;
  if (num == 1)
    nameWidth += 2;

  shortname = GetFixName(fsTitle, name, nameWidth);
}

/*
 * DrawTitle --
 *   Draw title.
 */
void Qvwm::DrawTitle(Bool focusChange)
{
  Point ptTitle;
  XRectangle ink, log;

  if (this == rootQvwm || !CheckFlags(TITLE))
    return;

  if (focusChange) {
    if (CheckFocus()) {
      if (TitlebarImage)
	imgTitle->SetBackground(None);

      if (TitlebarActiveImage)
	imgActiveTitle->SetBackground(title);
      else if (GradTitlebar) {
	if (pixActiveGrad == None)
	  pixActiveGrad = CreateGradPixmap(gradActivePattern, rcTitle.width,
					   title);
	XSetWindowBackgroundPixmap(display, title, pixActiveGrad);
      }
      else
	XSetWindowBackground(display, title, TitlebarActiveColor.pixel);
    }
    else {
      if (TitlebarActiveImage)
	imgActiveTitle->SetBackground(None);

      if (TitlebarImage)
	imgTitle->SetBackground(title);
      else if (GradTitlebar) {
	if (pixGrad == None)
	  pixGrad = CreateGradPixmap(gradPattern, rcTitle.width, title);
	XSetWindowBackgroundPixmap(display, title, pixGrad);
      }
      else
	XSetWindowBackground(display, title, TitlebarColor.pixel);
    }
  }
    
  XClearWindow(display, title);

  DrawCtrlMenuMark();
  
  /*
   * Draw the title text.
   */
  XmbTextExtents(fsTitle, shortname, strlen(shortname), &ink, &log);
  if (CheckFlags(CTRL_MENU))
    ptTitle.x = TaskbarButton::SYMBOL_SIZE + 4;
  else
    ptTitle.x = 4;
  ptTitle.y = (TITLE_HEIGHT - log.height)/2 - log.y;
  
  if (CheckFocus())
    XSetForeground(display, gc, TitleStringActiveColor.pixel);
  else
    XSetForeground(display, gc, TitleStringColor.pixel);
  
  XmbDrawString(display, title, fsTitle, gc, ptTitle.x, ptTitle.y,
		shortname, strlen(shortname));

  if (strlen(name) > strlen(shortname)) {
    XRectangle xr[3] =
    {{ptTitle.x + log.width + 2, ptTitle.y - 2, 1, 2},
     {ptTitle.x + log.width + 5, ptTitle.y - 2, 1, 2},
     {ptTitle.x + log.width + 8, ptTitle.y - 2, 1, 2}};
    
    XFillRectangles(display, title, gc, xr, 3);
  }
}

/*
 * MotionTitlebar --
 *   Move the titlebar to dest slowly.
 */
void Qvwm::MotionTitlebar(const Rect& rcSrc, const Rect& rcDest)
{
  Window motionBarWin;
  Rect rcNew;
  XSetWindowAttributes attributes;
  unsigned long valueMask;
  QvImage* imgIcon = GetIconImage();

  if (!TitlebarMotion)
    return;

  XSync(display, 0);

  attributes.save_under = DoesSaveUnders(ScreenOfDisplay(display, screen));
  attributes.background_pixel = TitlebarActiveColor.pixel;
  valueMask = CWSaveUnder | CWBackPixel;

  /*
   * Create a motion title bar.
   */
  motionBarWin = XCreateWindow(display, root, rcSrc.x, rcSrc.y,
			       rcSrc.width, TITLE_HEIGHT,
			       0, CopyFromParent, InputOutput, CopyFromParent, 
			       valueMask, &attributes);

  XMapRaised(display, motionBarWin);

  /*
   * Move the titlebar slowly.
   */
  XSetForeground(display, gc, white.pixel);

  for (int i = 1; i < TitlebarMotionSpeed; i++) {
    rcNew.x = rcSrc.x + (rcDest.x - rcSrc.x) * i / TitlebarMotionSpeed;
    rcNew.y = rcSrc.y + (rcDest.y - rcSrc.y) * i / TitlebarMotionSpeed;
    rcNew.width = rcSrc.width + (rcDest.width - rcSrc.width) * i
      / TitlebarMotionSpeed;

    XMoveResizeWindow(display, motionBarWin,
		      rcNew.x, rcNew.y, rcNew.width, TITLE_HEIGHT);
    imgIcon->Display(motionBarWin, Point(2, 1));

    /*
     * If you don't have this function, motion title bar may be underneath
     * main window.
     */
    XRaiseWindow(display, motionBarWin);

    XFlush(display);
    usleep(10000);
  }

  XDestroyWindow(display, motionBarWin);
}

void Qvwm::TitleButton1Press(Time clickTime, const Point& ptRoot)
{
  /*
   * When double click.
   */
  if (!CheckFlags(TRANSIENT) &&
      IsDoubleClick(titleClickTime, clickTime, event.ptPrevRoot, ptRoot)) {
    ASSERT(fButton[1]);
    fButton[1]->SetState(Button::NORMAL);
    if (CheckStatus(MAXIMIZE_WINDOW)) {
      /*
       * Restore if maximun window.
       */
      fButton[1]->ChangeImage(FrameButton::MAXIMIZE);
      RestoreWindow();
    }
    else {
      /*
       * Maximize if normal window.
       */
      fButton[1]->ChangeImage(FrameButton::RESTORE);
      MaximizeWindow();
    }
  }
  titleClickTime = clickTime;
}

void Qvwm::CreateTitlebarPixmap()
{
  if (TitlebarImage) {
    imgTitlebar = CreateImageFromFile(TitlebarImage, timer);
    if (imgTitlebar == None) {
      delete [] TitlebarImage;
      TitlebarImage = NULL;
    }
  }
  if (TitlebarActiveImage) {
    imgActiveTitlebar = CreateImageFromFile(TitlebarActiveImage, timer);
    if (imgActiveTitlebar == None) {
      delete [] TitlebarActiveImage;
      TitlebarActiveImage = NULL;
    }
  }
}

// titlebar general routines
Pixmap CreateGradPixmap(unsigned long* pat, int width, Window win)
{
  Pixmap pix = XCreatePixmap(display, win, width, 4, depth);
  GC gc = XCreateGC(display, pix, 0, 0);
  XImage* image = XGetImage(display, pix, 0, 0, width, 4, AllPlanes, XYPixmap);
  int* xIndex = new int[width];
  int level = (GradTitlebarColors - 1) / 3;
  int k = 0, sw;

  // calculate the position of base pattern
  for (int i = 0; i < level; i++) {
    int base = i * 28;
    for (int j = 0; j < 28;) {
      switch (j) {
	// # of pattern is 2
      case 0: case 6: case 8: case 10: case 16: case 18: case 20: case 26:
	sw = 0;
	for (; k < (base + j + 2) * width / (level * 28) - 1; k++) {
	  xIndex[k] = base + j + sw;
	  sw = (sw + 1) & 1;
	}
	j += 2;
	break;

	// # of pattern is 4
      case 2: case 12: case 22:
	sw = 0;
	for (; k < (base + j + 4) * width / (level * 28) - 1; k++) {
	  xIndex[k] = base + j + sw;
	  sw = (sw + 1) & 3;
	}
	j += 4;
	break;
      }
    }
  }
  for (; k < width; k++)
    xIndex[k] = level * 28 - 1;

  for (int x = 0; x < width; x++)
    for (int y = 0; y < 4; y++)
      XPutPixel(image, x, y, pat[xIndex[x] * 4 + y]);

  XPutImage(display, pix, gc, image, 0, 0, 0, 0, width, 4);
  XDestroyImage(image);
  XFreeGC(display, gc);
  delete [] xIndex;

  return pix;
}

static unsigned long* CreatePattern(XColor base1, XColor base2);
static void SetPattern(unsigned long* pat, int offset, unsigned long p[]);

void CreateGradPattern()
{
  if (GradTitlebarColors == 1) {
    GradTitlebar = False;
    return;
  }

  gradPattern = CreatePattern(TitlebarColor, TitlebarColor2);
  gradActivePattern = CreatePattern(TitlebarActiveColor,
				    TitlebarActiveColor2);
}

static unsigned long* CreatePattern(XColor base1, XColor base2)
{
  int level = (GradTitlebarColors - 1) / 3;

  // need 4 colors at minimum
  if (level == 0) {
    GradTitlebarColors = 4;  // XXX
    level = 1;
  }

  unsigned long* pat = new unsigned long[28 * level * 4];
  XColor* cMajor = new XColor[level + 1];
  int diffRed = base2.red - base1.red;
  int diffGreen = base2.green - base1.green;
  int diffBlue = base2.blue - base1.blue;
  int i, j;

  // major color allocation
  for (i = 0; i < (level + 1) / 2; i++) {
    XColor* sub = (i == 0) ? NULL : &cMajor[i - 1];

    CreateColor(base1.red   + diffRed   * i / level,
		base1.green + diffGreen * i / level,
		base1.blue  + diffBlue  * i / level,
		&cMajor[i], sub, "titlebar gradation");
  }
  for (j = level; j >= i; j--) {
    XColor* sub = (j == level) ? NULL : &cMajor[j + 1];

    CreateColor(base1.red   + diffRed   * j / level,
		base1.green + diffGreen * j / level,
		base1.blue  + diffBlue  * j / level,
		&cMajor[j], sub, "titlebar gradation");
  }

  // minor color allocation
  for (i = 0; i < level; i++) {
    double scale1 = (i + 0.33) / level;
    double scale2 = (i + 0.66) / level;
    XColor cMinor[2];
    unsigned long p[4];

    CreateColor(base1.red   + int(diffRed   * scale1),
		base1.green + int(diffGreen * scale1),
		base1.blue  + int(diffBlue  * scale1),
		&cMinor[0], &cMajor[i]);

    CreateColor(base1.red   + int(diffRed   * scale2),
		base1.green + int(diffGreen * scale2),
		base1.blue  + int(diffBlue  * scale2),
		&cMinor[1], &cMajor[i + 1]);

    p[0] = cMajor[i].pixel;
    p[1] = cMinor[0].pixel;
    p[2] = cMinor[1].pixel;
    p[3] = cMajor[i + 1].pixel;

    SetPattern(pat, 28 * i, p);
  }

  delete [] cMajor;
  
  return pat;
}

static void SetPattern(unsigned long* pat, int offset, unsigned long p[])
{
  static int table[4][28] =
    {{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 2,
       1, 2, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3 },
     { 0, 0, 2, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 1,
       2, 1, 3, 1, 3, 1, 3, 1, 3, 2, 3, 1, 3, 3 },
     { 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 2,
       1, 2, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3 },
     { 0, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1,
       2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 2, 3, 3 }};
  
  for (int x = 0; x < 28; x++)
    for (int y = 0; y < 4; y++)
      pat[(x + offset) * 4 + y] = p[table[y][x]];
}


syntax highlighted by Code2HTML, v. 0.9.1