<commit_before>/*
  Copyright 2008 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
#include <algorithm>

#include <QtCore/QUrl>
#include <QtGui/QDesktopWidget>
#include <QtGui/QPalette>
#include <QtGui/QPainter>
#include <QtGui/QLayout>
#include <QtGui/QMouseEvent>
#include <ggadget/graphics_interface.h>
#include <ggadget/main_loop_interface.h>
#include <ggadget/gadget.h>
#include <ggadget/clip_region.h>
#include <ggadget/math_utils.h>
#include <ggadget/string_utils.h>
#include <ggadget/qt/qt_canvas.h>
#include <ggadget/qt/utilities.h>
#include <ggadget/qt/qt_menu.h>
#include "qt_view_widget.h"

#if defined(Q_WS_X11) && defined(HAVE_X11)
#include <QtGui/QX11Info>
#include <QtGui/QBitmap>
#include <X11/extensions/shape.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#endif

namespace ggadget {
namespace qt {

static const double kDragThreshold = 3;
#ifdef _DEBUG
static const uint64_t kFPSCountDuration = 5000;
#endif

// update input mask once per second.
static const uint64_t kUpdateMaskInterval = 1000;

QtViewWidget::QtViewWidget(ViewInterface *view, QtViewWidget::Flags flags)
    : view_(view),
      drag_files_(NULL),
      drag_urls_(NULL),
      composite_(flags.testFlag(QtViewWidget::FLAG_COMPOSITE)),
      movable_(flags.testFlag(QtViewWidget::FLAG_MOVABLE)),
      enable_input_mask_(false),
      support_input_mask_(flags.testFlag(QtViewWidget::FLAG_INPUT_MASK) &&
                          flags.testFlag(QtViewWidget::FLAG_COMPOSITE)),
      offscreen_pixmap_(NULL),
      mouse_drag_moved_(false),
      child_(NULL),
      zoom_(view->GetGraphics()->GetZoom()),
      last_mask_time_(0),
#ifdef _DEBUG
      last_fps_time_(0),
      draw_count_(0),
#endif
      mouse_down_hittest_(ViewInterface::HT_CLIENT),
      resize_drag_(false),
      old_width_(0),
      old_height_(0) {
  setMouseTracking(true);
  setAcceptDrops(true);
  AdjustToViewSize();
  if (!flags.testFlag(QtViewWidget::FLAG_WM_DECORATED)) {
    setWindowFlags(Qt::FramelessWindowHint);
    SetUndecoratedWMProperties();
  }
  setAttribute(Qt::WA_InputMethodEnabled);
  setAttribute(Qt::WA_OpaquePaintEvent);
  setAttribute(Qt::WA_NoSystemBackground);
  setAutoFillBackground(false);
}

QtViewWidget::~QtViewWidget() {
  DLOG("Widget freed");
  if (child_) {
    // Because I don't own child_, just let it go
    child_->setParent(NULL);
  }
  if (offscreen_pixmap_) delete offscreen_pixmap_;
  if (drag_files_) delete [] drag_files_;
  if (drag_urls_) delete [] drag_urls_;
}

static QRegion CreateClipRegion(const ClipRegion *view_region, double zoom) {
  QRegion qregion;
  QRect qrect;
  size_t count = view_region->GetRectangleCount();
  if (count) {
    Rectangle rect;
    for (size_t i = 0; i < count; ++i) {
      rect = view_region->GetRectangle(i);
      if (zoom != 1.0) {
        rect.Zoom(zoom);
        rect.Integerize(true);
      }
      qrect.setX(static_cast<int>(rect.x));
      qrect.setY(static_cast<int>(rect.y));
      qrect.setWidth(static_cast<int>(rect.w));
      qrect.setHeight(static_cast<int>(rect.h));
      qregion += qrect;
    }
  }
  return qregion;
}

void QtViewWidget::paintEvent(QPaintEvent *event) {
  if (!view_) return;
  QPainter p(this);
  view_->Layout();

  if (old_width_ != width() || old_height_ != height()) {
    view_->AddRectangleToClipRegion(
        Rectangle(0, 0, view_->GetWidth(), view_->GetHeight()));
    old_width_ = width();
    old_height_ = height();
    delete offscreen_pixmap_;
    offscreen_pixmap_ = NULL;
  }

  uint64_t current_time = GetGlobalMainLoop()->GetCurrentTime();
  if (enable_input_mask_ &&
      (current_time - last_mask_time_ > kUpdateMaskInterval)) {
    last_mask_time_ = current_time;
    // Only update input mask once per second.
    if (!offscreen_pixmap_) {
      offscreen_pixmap_ = new QPixmap(width(), height());
      offscreen_pixmap_->fill(Qt::transparent);
      view_->AddRectangleToClipRegion(
          Rectangle(0, 0, view_->GetWidth(), view_->GetHeight()));
    }

    QRegion clip_region = CreateClipRegion(view_->GetClipRegion(), zoom_);
    QPainter poff(offscreen_pixmap_);
    poff.setClipRegion(clip_region, Qt::IntersectClip);
    poff.setCompositionMode(QPainter::CompositionMode_Clear);
    poff.fillRect(rect(), Qt::transparent);
    poff.scale(zoom_, zoom_);

    QtCanvas canvas(width(), height(), &poff);
    view_->Draw(&canvas);
    SetInputMask(offscreen_pixmap_);

    p.setClipRegion(clip_region, Qt::IntersectClip);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    p.drawPixmap(0, 0, *offscreen_pixmap_);
  } else {
    QRegion clip_region = CreateClipRegion(view_->GetClipRegion(), zoom_);
    p.setClipRegion(clip_region, Qt::IntersectClip);
    p.setCompositionMode(QPainter::CompositionMode_Clear);
    p.fillRect(rect(), Qt::transparent);
    p.scale(zoom_, zoom_);
    QtCanvas canvas(width(), height(), &p);
    view_->Draw(&canvas);
  }

#ifdef _DEBUG
  ++draw_count_;
  uint64_t duration = current_time - last_fps_time_;
  if (duration >= kFPSCountDuration) {
    last_fps_time_ = current_time;
    DLOG("FPS of View %s: %f", view_->GetCaption().c_str(),
         static_cast<double>(draw_count_ * 1000) /
         static_cast<double>(duration));
    draw_count_ = 0;
  }
#endif
}

void QtViewWidget::mouseDoubleClickEvent(QMouseEvent * event) {
  if (!view_) return;
  Event::Type type;
  if (Qt::LeftButton == event->button())
    type = Event::EVENT_MOUSE_DBLCLICK;<commit_msg> r1580@suzhe-mac:  suzhe | 2009-05-25 00:59:44 +0800  - More fix to qt host.<commit_after>/*
  Copyright 2008 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
#include <algorithm>

#include <QtCore/QUrl>
#include <QtGui/QDesktopWidget>
#include <QtGui/QPalette>
#include <QtGui/QPainter>
#include <QtGui/QLayout>
#include <QtGui/QMouseEvent>
#include <ggadget/graphics_interface.h>
#include <ggadget/main_loop_interface.h>
#include <ggadget/gadget.h>
#include <ggadget/clip_region.h>
#include <ggadget/math_utils.h>
#include <ggadget/string_utils.h>
#include <ggadget/qt/qt_canvas.h>
#include <ggadget/qt/utilities.h>
#include <ggadget/qt/qt_menu.h>
#include "qt_view_widget.h"

#if defined(Q_WS_X11) && defined(HAVE_X11)
#include <QtGui/QX11Info>
#include <QtGui/QBitmap>
#include <X11/extensions/shape.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#endif

namespace ggadget {
namespace qt {

static const double kDragThreshold = 3;
#ifdef _DEBUG
static const uint64_t kFPSCountDuration = 5000;
#endif

// update input mask once per second.
static const uint64_t kUpdateMaskInterval = 1000;

QtViewWidget::QtViewWidget(ViewInterface *view, QtViewWidget::Flags flags)
    : view_(view),
      drag_files_(NULL),
      drag_urls_(NULL),
      composite_(flags.testFlag(QtViewWidget::FLAG_COMPOSITE)),
      movable_(flags.testFlag(QtViewWidget::FLAG_MOVABLE)),
      enable_input_mask_(false),
      support_input_mask_(flags.testFlag(QtViewWidget::FLAG_INPUT_MASK) &&
                          flags.testFlag(QtViewWidget::FLAG_COMPOSITE)),
      offscreen_pixmap_(NULL),
      mouse_drag_moved_(false),
      child_(NULL),
      zoom_(view->GetGraphics()->GetZoom()),
      last_mask_time_(0),
#ifdef _DEBUG
      last_fps_time_(0),
      draw_count_(0),
#endif
      mouse_down_hittest_(ViewInterface::HT_CLIENT),
      resize_drag_(false),
      old_width_(0),
      old_height_(0) {
  setMouseTracking(true);
  setAcceptDrops(true);
  AdjustToViewSize();
  if (!flags.testFlag(QtViewWidget::FLAG_WM_DECORATED)) {
    setWindowFlags(Qt::FramelessWindowHint);
    SetUndecoratedWMProperties();
  }
  setAttribute(Qt::WA_InputMethodEnabled);
  setAttribute(Qt::WA_OpaquePaintEvent);
  setAttribute(Qt::WA_NoSystemBackground);
  setAutoFillBackground(false);
}

QtViewWidget::~QtViewWidget() {
  DLOG("Widget freed");
  if (child_) {
    // Because I don't own child_, just let it go
    child_->setParent(NULL);
  }
  if (offscreen_pixmap_) delete offscreen_pixmap_;
  if (drag_files_) delete [] drag_files_;
  if (drag_urls_) delete [] drag_urls_;
}

static QRegion CreateClipRegion(const ClipRegion *view_region, double zoom) {
  QRegion qregion;
  QRect qrect;
  size_t count = view_region->GetRectangleCount();
  if (count) {
    Rectangle rect;
    for (size_t i = 0; i < count; ++i) {
      rect = view_region->GetRectangle(i);
      if (zoom != 1.0) {
        rect.Zoom(zoom);
        rect.Integerize(true);
      }
      qrect.setX(static_cast<int>(rect.x));
      qrect.setY(static_cast<int>(rect.y));
      qrect.setWidth(static_cast<int>(rect.w));
      qrect.setHeight(static_cast<int>(rect.h));
      qregion += qrect;
    }
  }
  return qregion;
}

void QtViewWidget::paintEvent(QPaintEvent *event) {
  if (!view_) return;
  QPainter p(this);
  view_->Layout();

  DLOG("paint: %p, ow:%d, oh:%d, w:%d, h:%d, vw:%f, vh:%f, uw:%d, uh:%d",
       view_, old_width_, old_height_, width(), height(),
       view_->GetWidth(), view_->GetHeight(),
       event->rect().width(), event->rect().height());

  if (old_width_ != width() || old_height_ != height() ||
      rect() != event->rect()) {
    view_->AddRectangleToClipRegion(
        Rectangle(0, 0, view_->GetWidth(), view_->GetHeight()));
    old_width_ = width();
    old_height_ = height();
    delete offscreen_pixmap_;
    offscreen_pixmap_ = NULL;
    p.setClipRect(rect());
  }

  uint64_t current_time = GetGlobalMainLoop()->GetCurrentTime();
  if (enable_input_mask_ &&
      (current_time - last_mask_time_ > kUpdateMaskInterval)) {
    last_mask_time_ = current_time;
    // Only update input mask once per second.
    if (!offscreen_pixmap_) {
      offscreen_pixmap_ = new QPixmap(width(), height());
      offscreen_pixmap_->fill(Qt::transparent);
      view_->AddRectangleToClipRegion(
          Rectangle(0, 0, view_->GetWidth(), view_->GetHeight()));
    }

    QRegion clip_region = CreateClipRegion(view_->GetClipRegion(), zoom_);
    QPainter poff(offscreen_pixmap_);
    poff.setClipRegion(clip_region);
    poff.setCompositionMode(QPainter::CompositionMode_Clear);
    if (composite_) {
      poff.fillRect(rect(), Qt::transparent);
    } else {
      poff.fillRect(rect(), palette().window());
    }
    poff.scale(zoom_, zoom_);

    QtCanvas canvas(width(), height(), &poff);
    view_->Draw(&canvas);
    SetInputMask(offscreen_pixmap_);

    p.setClipRegion(clip_region, Qt::UniteClip);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    p.drawPixmap(0, 0, *offscreen_pixmap_);
  } else {
    QRegion clip_region = CreateClipRegion(view_->GetClipRegion(), zoom_);
    p.setClipRegion(clip_region, Qt::UniteClip);
    p.setCompositionMode(QPainter::CompositionMode_Source);
    if (composite_) {
      p.fillRect(rect(), Qt::transparent);
    } else {
      p.fillRect(rect(), palette().window());
    }
    p.scale(zoom_, zoom_);
    QtCanvas canvas(width(), height(), &p);
    view_->Draw(&canvas);
  }

#ifdef _DEBUG
  ++draw_count_;
  uint64_t duration = current_time - last_fps_time_;
  if (duration >= kFPSCountDuration) {
    last_fps_time_ = current_time;
    DLOG("FPS of View %s: %f", view_->GetCaption().c_str(),
         static_cast<double>(draw_count_ * 1000) /
         static_cast<double>(duration));
    draw_count_ = 0;
  }
#endif
}

void QtViewWidget::mouseDoubleClickEvent(QMouseEvent * event) {
  if (!view_) return;
  Event::Type type;
  if (Qt::LeftButton == event->button())
    type = Event::EVENT_MOUSE_DBLCLICK;
{'fraction_non_alphanumeric': 0.13199935452638373, 'fraction_numerical': 0.010811683072454413, 'mean_word_length': 4.232165470662727, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 106, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5258768', 'n_tokens_mistral': 4275, 'n_tokens_neox': 4181, 'n_words': 836}