"Fossies" - the Fresh Open Source Software Archive

Member "google-gadgets-for-linux-0.11.2/ggadget/view_element.cc" (28 Dec 2009, 14177 Bytes) of package /linux/misc/old/google-gadgets-for-linux-0.11.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2   Copyright 2008 Google Inc.
    3 
    4   Licensed under the Apache License, Version 2.0 (the "License");
    5   you may not use this file except in compliance with the License.
    6   You may obtain a copy of the License at
    7 
    8        http://www.apache.org/licenses/LICENSE-2.0
    9 
   10   Unless required by applicable law or agreed to in writing, software
   11   distributed under the License is distributed on an "AS IS" BASIS,
   12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13   See the License for the specific language governing permissions and
   14   limitations under the License.
   15 */
   16 
   17 #include <vector>
   18 
   19 #include "view_element.h"
   20 #include "canvas_interface.h"
   21 #include "elements.h"
   22 #include "graphics_interface.h"
   23 #include "logger.h"
   24 #include "signals.h"
   25 #include "view.h"
   26 #include "view_host_interface.h"
   27 #include "math_utils.h"
   28 #include "small_object.h"
   29 #include "clip_region.h"
   30 
   31 namespace ggadget {
   32 
   33 static const double kMinimumScale = 0.5;
   34 static const double kMaximumScale = 2.0;
   35 
   36 class ViewElement::Impl : public SmallObject<> {
   37  public:
   38   Impl(ViewElement *owner, bool no_transparent)
   39     : owner_(owner),
   40       child_view_(NULL),
   41       scale_(1),
   42       no_transparent_(no_transparent),
   43       onsizing_called_(false),
   44       onsizing_result_(false),
   45       onsizing_width_request_(0),
   46       onsizing_height_request_(0),
   47       onsizing_width_result_(0),
   48       onsizing_height_result_(0),
   49       abs_x0_(0),
   50       abs_y0_(0),
   51       abs_x1_(0),
   52       abs_y1_(0),
   53       onsize_connection_(NULL),
   54       onopen_connection_(NULL),
   55       on_add_clip_rect_connection_(NULL) {
   56     on_add_clip_rect_connection_ =
   57         owner_->GetView()->ConnectOnAddRectangleToClipRegion(
   58             NewSlot(this, &Impl::OnAddClipRect));
   59   }
   60 
   61   ~Impl() {
   62     if (onsize_connection_)
   63       onsize_connection_->Disconnect();
   64     if (onopen_connection_)
   65       onopen_connection_->Disconnect();
   66     if (on_add_clip_rect_connection_)
   67       on_add_clip_rect_connection_->Disconnect();
   68   }
   69 
   70   void OnChildViewOpen() {
   71     UpdateScaleAndSize();
   72     // Inform parent view to adjust its size.
   73     // The parent view might be decorated view or sidebar.
   74     child_view_->GetViewHost()->QueueResize();
   75   }
   76 
   77   void UpdateScaleAndSize() {
   78     if (child_view_) {
   79       scale_ = child_view_->GetGraphics()->GetZoom() /
   80                owner_->GetView()->GetGraphics()->GetZoom();
   81 
   82       double width = child_view_->GetWidth() * scale_;
   83       double height = child_view_->GetHeight() * scale_;
   84 
   85       owner_->SetPixelWidth(width);
   86       owner_->SetPixelHeight(height);
   87     } else {
   88       scale_ = 1.0;
   89     }
   90   }
   91 
   92   void OnAddClipRect(double x, double y, double w, double h) {
   93     if (child_view_) {
   94       double r[8];
   95       owner_->ViewCoordToChildViewCoord(x, y, &r[0], &r[1]);
   96       owner_->ViewCoordToChildViewCoord(x, y + h, &r[2], &r[3]);
   97       owner_->ViewCoordToChildViewCoord(x + w, y + h, &r[4], &r[5]);
   98       owner_->ViewCoordToChildViewCoord(x + w, y, &r[6], &r[7]);
   99 
  100       Rectangle child_rect = Rectangle::GetPolygonExtents(4, r);
  101       child_view_->AddRectangleToClipRegion(child_rect);
  102     }
  103   }
  104 
  105   ViewElement *owner_;
  106   View *child_view_; // This view is not owned by the element.
  107   double scale_;
  108   bool no_transparent_;
  109 
  110   bool onsizing_called_;
  111   bool onsizing_result_;
  112   double onsizing_width_request_;
  113   double onsizing_height_request_;
  114   double onsizing_width_result_;
  115   double onsizing_height_result_;
  116 
  117   double abs_x0_;
  118   double abs_y0_;
  119   double abs_x1_;
  120   double abs_y1_;
  121 
  122   Connection *onsize_connection_;
  123   Connection *onopen_connection_;
  124 
  125   Connection *on_add_clip_rect_connection_;
  126 };
  127 
  128 ViewElement::ViewElement(View *parent_view, View *child_view,
  129                          bool no_transparent)
  130   // Only 1 child so no need to involve Elements here.
  131   : BasicElement(parent_view, "view", NULL, false),
  132     impl_(new Impl(this, no_transparent)) {
  133   SetEnabled(true);
  134   SetChildView(child_view);
  135 }
  136 
  137 ViewElement::~ViewElement() {
  138   delete impl_;
  139   impl_ = NULL;
  140 }
  141 
  142 void ViewElement::SetChildView(View *child_view) {
  143   if (child_view == impl_->child_view_)
  144     return;
  145 
  146   if (impl_->onsize_connection_) {
  147     impl_->onsize_connection_->Disconnect();
  148     impl_->onsize_connection_ = NULL;
  149   }
  150 
  151   if (impl_->onopen_connection_) {
  152     impl_->onopen_connection_->Disconnect();
  153     impl_->onopen_connection_ = NULL;
  154   }
  155 
  156   // Hook onopen event to do the first time initialization.
  157   // Because when View is initialized from XML, the event is disabled, so the
  158   // onsize event can't be received.
  159   if (child_view) {
  160     impl_->onsize_connection_ = child_view->ConnectOnSizeEvent(
  161         NewSlot(impl_, &Impl::UpdateScaleAndSize));
  162     impl_->onopen_connection_ = child_view->ConnectOnOpenEvent(
  163         NewSlot(impl_, &Impl::OnChildViewOpen));
  164     if (GetView()->GetFocusedElement() != this)
  165       child_view->SetFocus(NULL);
  166   }
  167 
  168   impl_->child_view_ = child_view;
  169   impl_->UpdateScaleAndSize();
  170   QueueDraw();
  171 }
  172 
  173 View *ViewElement::GetChildView() const {
  174   return impl_->child_view_;
  175 }
  176 
  177 bool ViewElement::OnSizing(double *width, double *height) {
  178   ASSERT(width && height);
  179   if (*width <= 0 || *height <= 0)
  180     return false;
  181 
  182   // Any size is allowed if there is no child view.
  183   if (!impl_->child_view_)
  184     return true;
  185 
  186   if (impl_->onsizing_called_ &&
  187       impl_->onsizing_width_request_ == *width &&
  188       impl_->onsizing_height_request_ == *height) {
  189     *width = impl_->onsizing_width_result_;
  190     *height = impl_->onsizing_height_result_;
  191     return impl_->onsizing_result_;
  192   }
  193 
  194   impl_->onsizing_called_ = true;
  195   impl_->onsizing_width_request_ = *width;
  196   impl_->onsizing_height_request_ = *height;
  197 
  198   double child_width;
  199   double child_height;
  200   bool ret = false;
  201   ViewInterface::ResizableMode mode = impl_->child_view_->GetResizable();
  202 
  203   // If child view is resizable then just delegate OnSizing request to child
  204   // view.
  205   // The resizable view might also be zoomed, so count the scale factor in.
  206   if (mode == ViewInterface::RESIZABLE_TRUE ||
  207       mode == ViewInterface::RESIZABLE_KEEP_RATIO) {
  208     child_width = *width / impl_->scale_;
  209     child_height = *height / impl_->scale_;
  210     ret = impl_->child_view_->OnSizing(&child_width, &child_height);
  211     *width = child_width * impl_->scale_;
  212     *height = child_height * impl_->scale_;
  213   } else {
  214     // Otherwise adjust the width or height to maintain the aspect ratio of
  215     // child view.
  216     child_width = impl_->child_view_->GetWidth();
  217     child_height = impl_->child_view_->GetHeight();
  218     double aspect_ratio = child_width / child_height;
  219 
  220     // Keep the shorter edge unchanged.
  221     if (*width / *height < aspect_ratio) {
  222       *height = *width / aspect_ratio;
  223     } else {
  224       *width = *height * aspect_ratio;
  225     }
  226 
  227     // Don't allow scale to too small.
  228     double new_scale = *width / child_width;
  229     if (new_scale < kMinimumScale || new_scale > kMaximumScale) {
  230       new_scale = Clamp(new_scale, kMinimumScale, kMaximumScale);
  231       *width = child_width * new_scale;
  232       *height = child_height * new_scale;
  233     }
  234 
  235     // Always returns true when zooming to get smooth zoom effect.
  236     ret = true;
  237   }
  238 
  239   impl_->onsizing_width_result_ = *width;
  240   impl_->onsizing_height_result_ = *height;
  241   impl_->onsizing_result_ = ret;
  242   return ret;
  243 }
  244 
  245 void ViewElement::SetSize(double width, double height) {
  246   double old_width = GetPixelWidth();
  247   double old_height = GetPixelHeight();
  248 
  249   if (width <= 0 || height <= 0) return;
  250   if (width == old_width && height == old_height) return;
  251 
  252   // If there is no child view, then just adjust BasicElement's size.
  253   if (!impl_->child_view_) {
  254     SetPixelWidth(width);
  255     SetPixelHeight(height);
  256     return;
  257   }
  258 
  259   ViewInterface::ResizableMode mode = impl_->child_view_->GetResizable();
  260   if (mode == ViewInterface::RESIZABLE_TRUE ||
  261       mode == ViewInterface::RESIZABLE_KEEP_RATIO) {
  262     // The resizable view might also be zoomed, so count the scale factor in.
  263     impl_->child_view_->SetSize(width / impl_->scale_, height / impl_->scale_);
  264     impl_->UpdateScaleAndSize();
  265   } else {
  266     double child_width = impl_->child_view_->GetWidth();
  267     double child_height = impl_->child_view_->GetHeight();
  268     double aspect_ratio = child_width / child_height;
  269 
  270     // Calculate the scale factor according to the shorter edge.
  271     if (width / height < aspect_ratio)
  272       SetScale(width / child_width);
  273     else
  274       SetScale(height / child_height);
  275   }
  276 
  277   impl_->onsizing_called_ = false;
  278   QueueDraw();
  279 }
  280 
  281 void ViewElement::SetScale(double scale) {
  282   // Only set scale if child view is available.
  283   scale = Clamp(scale, kMinimumScale, kMaximumScale);
  284   if (impl_->child_view_ && scale != impl_->scale_) {
  285     double new_zoom = GetView()->GetGraphics()->GetZoom() * scale;
  286     impl_->child_view_->GetGraphics()->SetZoom(new_zoom);
  287     impl_->child_view_->MarkRedraw();
  288     impl_->UpdateScaleAndSize();
  289     // Inform parent view to adjust its size.
  290     // The parent view might be decorated view or sidebar.
  291     impl_->child_view_->GetViewHost()->QueueResize();
  292     QueueDraw();
  293   }
  294 }
  295 
  296 double ViewElement::GetScale() const {
  297   return impl_->scale_;
  298 }
  299 
  300 void ViewElement::ChildViewCoordToViewCoord(
  301     double child_x, double child_y, double *parent_x, double *parent_y) const {
  302   child_x *= impl_->scale_;
  303   child_y *= impl_->scale_;
  304 
  305   SelfCoordToViewCoord(child_x, child_y, parent_x, parent_y);
  306 }
  307 
  308 void ViewElement::ViewCoordToChildViewCoord(
  309     double view_x, double view_y, double *child_x, double *child_y) const {
  310   ViewCoordToSelfCoord(view_x, view_y, child_x, child_y);
  311   *child_x /= impl_->scale_;
  312   *child_y /= impl_->scale_;
  313 }
  314 
  315 ViewInterface::HitTest ViewElement::GetHitTest(double x, double y) const {
  316   // Assume GetHitTest() will be called immediately after calling
  317   // OnMouseEvent().
  318   if (impl_->child_view_) {
  319     // If the ViewElement's parent is a Sidebar, then in most case,
  320     // the child view is a view decorator, then it's necessary to
  321     // /return HT_NOWHERE instead of HT_TRANSPARENT to make sure that
  322     // the child view decorator won't hide the decorator while the mouse
  323     // pointer is still inside it.
  324     ViewInterface::HitTest hittest = impl_->child_view_->GetHitTest();
  325     return (hittest == ViewInterface::HT_TRANSPARENT &&
  326             impl_->no_transparent_) ? ViewInterface::HT_NOWHERE : hittest;
  327   }
  328 
  329   return BasicElement::GetHitTest(x, y);
  330 }
  331 
  332 void ViewElement::Layout() {
  333   BasicElement::Layout();
  334   if (impl_->child_view_) {
  335     impl_->child_view_->Layout();
  336     const ClipRegion *region = impl_->child_view_->GetClipRegion();
  337     if (region && !region->IsEmpty()) {
  338       if (1.0 == impl_->scale_) {
  339         QueueDrawRegion(*region);
  340       } else {
  341         ClipRegion scaled_region(*region);
  342         scaled_region.Zoom(impl_->scale_);
  343         QueueDrawRegion(scaled_region);
  344       }
  345     }
  346   }
  347 }
  348 
  349 void ViewElement::QueueDrawChildView() {
  350   if (impl_->child_view_) {
  351     GetView()->QueueDraw();
  352   }
  353 }
  354 
  355 void ViewElement::MarkRedraw() {
  356   BasicElement::MarkRedraw();
  357   if (impl_->child_view_)
  358     impl_->child_view_->MarkRedraw();
  359 }
  360 
  361 void ViewElement::DoDraw(CanvasInterface *canvas) {
  362   if (impl_->child_view_) {
  363     if (impl_->scale_ != 1)
  364       canvas->ScaleCoordinates(impl_->scale_, impl_->scale_);
  365     bool clip_enabled = GetView()->IsClipRegionEnabled();
  366     impl_->child_view_->EnableClipRegion(clip_enabled);
  367     impl_->child_view_->Draw(canvas);
  368     impl_->child_view_->EnableClipRegion(clip_enabled);
  369   }
  370 }
  371 
  372 EventResult ViewElement::OnMouseEvent(const MouseEvent &event,
  373                                       bool direct,
  374                                       BasicElement **fired_element,
  375                                       BasicElement **in_element,
  376                                       ViewInterface::HitTest *hittest) {
  377   if (!impl_->child_view_)
  378     return BasicElement::OnMouseEvent(event, direct, fired_element,
  379                                       in_element, hittest);
  380 
  381   // child view must process the mouse event first, so that the hittest value
  382   // can be updated correctly.
  383   EventResult result1 = EVENT_RESULT_UNHANDLED;
  384   if (impl_->scale_ != 1.) {
  385     MouseEvent new_event(event);
  386     new_event.SetX(event.GetX() / impl_->scale_);
  387     new_event.SetY(event.GetY() / impl_->scale_);
  388     result1 = impl_->child_view_->OnMouseEvent(new_event);
  389   } else {
  390     result1 = impl_->child_view_->OnMouseEvent(event);
  391   }
  392 
  393   EventResult result2 = BasicElement::OnMouseEvent(event, direct, fired_element,
  394                                                    in_element, hittest);
  395 
  396   return std::max(result1, result2);
  397 }
  398 
  399 EventResult ViewElement::OnDragEvent(const DragEvent &event, bool direct,
  400                                      BasicElement **fired_element) {
  401   GGL_UNUSED(direct);
  402   if (!impl_->child_view_)
  403     return EVENT_RESULT_UNHANDLED;
  404 
  405   Event::Type type = event.GetType();
  406 
  407   // View doesn't accept DRAG_OVER event, so converts it to DRAG_MOTION.
  408   DragEvent new_event(type == Event::EVENT_DRAG_OVER ?
  409                       Event::EVENT_DRAG_MOTION : type,
  410                       event.GetX() / impl_->scale_,
  411                       event.GetY() / impl_->scale_);
  412   new_event.SetDragFiles(event.GetDragFiles());
  413   new_event.SetDragUrls(event.GetDragUrls());
  414   new_event.SetDragText(event.GetDragText());
  415 
  416   EventResult result = impl_->child_view_->OnDragEvent(new_event);
  417 
  418   if (result != EVENT_RESULT_UNHANDLED)
  419     *fired_element = this;
  420 
  421   return result;
  422 }
  423 
  424 bool ViewElement::OnAddContextMenuItems(MenuInterface *menu) {
  425   if (impl_->child_view_)
  426     return impl_->child_view_->OnAddContextMenuItems(menu);
  427   return false;
  428 }
  429 
  430 EventResult ViewElement::OnKeyEvent(const KeyboardEvent &event) {
  431   if (impl_->child_view_)
  432     return impl_->child_view_->OnKeyEvent(event);
  433   return EVENT_RESULT_UNHANDLED;
  434 }
  435 
  436 EventResult ViewElement::OnOtherEvent(const Event &event) {
  437   if (impl_->child_view_)
  438     return impl_->child_view_->OnOtherEvent(event);
  439   return EVENT_RESULT_UNHANDLED;
  440 }
  441 
  442 void ViewElement::GetDefaultSize(double *width, double *height) const {
  443   if (impl_->child_view_) {
  444     *width  = impl_->child_view_->GetWidth() * impl_->scale_;
  445     *height = impl_->child_view_->GetHeight() * impl_->scale_;
  446   } else {
  447     BasicElement::GetDefaultSize(width, height);
  448   }
  449 }
  450 
  451 } // namespace ggadget