Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

# background_wnd.cpp

    00001 // background_wnd.cpp
    00002
    00003 // Copyright (C) 2004 Steven Weiß (steven11@gmx.de)
    00004 //
    00005 // Permission to copy, use, sell and distribute this software is granted
    00006 // provided this copyright notice appears in all copies.
    00007 // Permission to modify the code and to distribute modified code is granted
    00008 // provided this copyright notice appears in all copies, and a notice
    00009 // that the code was modified is included with the copyright notice.
    00010 //
    00011 // This software is provided "as is" without express or implied warranty,
    00012 // and with no claim as to its suitability for any purpose.
    00013
    00014 // version history in version.txt
    00015
    00016
    00017 #pragma once
    00018 #include <background_wnd/detail/include.hpp>
    00019 #include <background_wnd/background_wnd.hpp>
    00020
    00021 #include <win32gui/window.hpp>                    // win32gui
    00022 #include <win32gui/event_handler.hpp>
    00023
    00024
    00025 namespace win32 { namespace gui {
    00026
    00027
    00028 // ============================================================================================================= //
    00029 // background_wnd-class
    00030
    00061 // ========================================================================================================== //
    00062 // Public interface
    00063
    00064 // c'tor
    00065 background_wnd::background_wnd() : cr_bkgnd_(RGB(255, 255, 255)), bRedraw_on_changes_(true), next_index_(0)
    00066 {}
    00067
    00069 void background_wnd::bk_color(COLORREF cr)
    00070 {
    00071      if (cr != cr_bkgnd_) {
    00072           cr_bkgnd_ = cr;
    00073           if (bRedraw_on_changes_)           // redraw
    00074                redraw_painters();
    00075      }
    00076 }
    00077
    00079 COLORREF background_wnd::bk_color() const
    00080 {
    00081      return cr_bkgnd_;
    00082 }
    00083
    00085 unsigned background_wnd::add_painter(const painter_base& painter)
    00086 {
    00087      return add_raw_painter(painter.clone());
    00088 }
    00089
    00093 void background_wnd::delete_painter(unsigned idx)
    00094 {
    00095      coll_painters_.erase( find_painter_info_iterator(idx) );
    00096      if (bRedraw_on_changes_)                // redraw
    00097           redraw_painters();
    00098 }
    00099
    00101 void background_wnd::delete_all_painters()
    00102 {
    00103      coll_painters_.clear();
    00104      if (bRedraw_on_changes_)                // redraw
    00105           redraw_painters();
    00106 }
    00107
    00111 painter_base& background_wnd::get_painter(unsigned idx) const
    00112 {
    00113      return find_painter_info(idx).get_painter();
    00114 }
    00115
    00116
    00121 std::vector<unsigned> background_wnd::get_indices() const
    00122 {
    00123      std::vector<unsigned> indices;
    00124      indices.reserve(coll_painters_.size());
    00125      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it)
    00126           indices.push_back( (*it)->idx_ );
    00127      return indices;
    00128 }
    00129
    00138 void background_wnd::drawing_rect(unsigned idx, const rectangle& rc)
    00139 {
    00140      find_painter_info(idx).rc_drawing_ = rc;
    00141      if (bRedraw_on_changes_)                // redraw
    00142           redraw_painters();
    00143 }
    00144
    00150 rectangle background_wnd::drawing_rect(unsigned idx) const
    00151 {
    00152      return find_painter_info(idx).rc_drawing_;
    00153 }
    00154
    00158 bool background_wnd::is_painter_visible(unsigned idx) const
    00159 {
    00160      return find_painter_info(idx).bVisible_;
    00161 }
    00162
    00166 void background_wnd::show_painter(unsigned idx, bool bVisible)
    00167 {
    00168      painter_info& info = find_painter_info(idx);
    00169     if ( info.bVisible_ != bVisible) {
    00170         info.bVisible_ = bVisible;
    00171         if (bRedraw_on_changes_)                  // redraw
    00172                redraw_painters();
    00173     }
    00174 }
    00175
    00180 void background_wnd::redraw_on_changes(bool bRedraw)
    00181 {
    00182      bRedraw_on_changes_ = bRedraw;
    00183 }
    00184
    00186 bool background_wnd::redraw_on_changes() const
    00187 {
    00188      return bRedraw_on_changes_;
    00189 }
    00190
    00192 void background_wnd::redraw_painters()
    00193 {
    00194      if (mem_dc_) {
    00195           // fill background
    00196           HBRUSH hbr = CreateSolidBrush(cr_bkgnd_);
    00197           FillRect(mem_dc_, &window_rect(rel_to_self), hbr);
    00198           DeleteObject((HGDIOBJ) hbr);
    00199
    00200           // redraw DC and invalidate window
    00201           redraw_mem_dc();
    00202           redraw();
    00203      }
    00204 }
    00205
    00211 void background_wnd::transparency(unsigned idx, unsigned transparency)
    00212 {
    00213      painter_info& info = find_painter_info(idx);
    00214
    00215      transparency = min<unsigned> (255, transparency);
    00216      if (info.transparency_ != transparency) {
    00217           info.transparency_ = transparency;
    00218           if (bRedraw_on_changes_)           // redraw
    00219                redraw_painters();
    00220      }
    00221 }
    00222
    00226 unsigned background_wnd::transparency(unsigned idx) const
    00227 {
    00228      return find_painter_info(idx).transparency_;
    00229 }
    00230
    00232 // Z-Order functions
    00233 // note: painters at the end of the sequence are on top of the Z-Order
    00234
    00241 void background_wnd::zorder_to_front(unsigned idx)
    00242 {
    00243      iterator it = find_painter_info_iterator(idx);
    00244      if (it != coll_painters_.end() - 1) {
    00245         PainterInfoPtr p = *it;                   // lovely boost::shared_ptr's copy c'tor doesn't throw :-)
    00246         coll_painters_.erase(it);
    00247         coll_painters_.push_back(p);
    00248
    00249           if (bRedraw_on_changes_)           // redraw
    00250                redraw_painters();
    00251     }
    00252 }
    00253
    00260 void background_wnd::zorder_to_back(unsigned idx)
    00261 {
    00262      iterator it = find_painter_info_iterator(idx);
    00263     if (it != coll_painters_.begin()) {
    00264         PainterColl tmp;
    00265         tmp.reserve(coll_painters_.size());
    00266         tmp.push_back(*it);
    00267         std::copy( coll_painters_.begin(), it, std::back_inserter(tmp) );
    00268           std::copy( it + 1, coll_painters_.end(), std::back_inserter(tmp) );
    00269         coll_painters_.swap(tmp);
    00270
    00271           if (bRedraw_on_changes_)           // redraw
    00272                redraw_painters();
    00273     }
    00274 }
    00275
    00282 void background_wnd::zorder_forward(unsigned idx)
    00283 {
    00284      iterator it = find_painter_info_iterator(idx);
    00285      if (it != coll_painters_.end() - 1) {
    00286           std::swap(*it, *(it + 1));
    00287
    00288           if (bRedraw_on_changes_)           // redraw
    00289                redraw_painters();
    00290      }
    00291 }
    00292
    00299 void background_wnd::zorder_backward(unsigned idx)
    00300 {
    00301      iterator it = find_painter_info_iterator(idx);
    00302      if (it != coll_painters_.begin()) {
    00303           std::swap(*it, *(it - 1));
    00304
    00305           if (bRedraw_on_changes_)           // redraw
    00306                redraw_painters();
    00307      }
    00308 }
    00309
    00310
    00311 // ========================================================================================================== //
    00312 // Protected interface
    00313
    00317 bg_wnd::memory_dc& background_wnd::get_mem_dc()
    00318 {
    00319     return mem_dc_;
    00320 }
    00321
    00325 const bg_wnd::memory_dc& background_wnd::get_mem_dc() const
    00326 {
    00327     return mem_dc_;
    00328 }
    00329
    00330
    00331 // ========================================================================================================== //
    00332 // Implementation
    00333
    00334 // Adds a painter and returns the index of this new painter
    00335 unsigned background_wnd::add_raw_painter(painter_base* pPainter)
    00336 {
    00337      try {
    00338           coll_painters_.push_back( PainterInfoPtr(new painter_info(next_index_, pPainter)) );
    00339           ++next_index_;
    00340      }
    00341      catch (...) {
    00342           delete pPainter;
    00343           throw;
    00344      }
    00345
    00346      if (bRedraw_on_changes_)                // redraw
    00347           redraw_painters();
    00348      return next_index_ - 1;
    00349 }
    00350
    00351 // finds a painter_info by index
    00352 background_wnd::iterator background_wnd::find_painter_info_iterator(unsigned idx) const
    00353 {
    00354      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it) {
    00355         if ((*it)->idx_ == idx)
    00356             return it;
    00357     }
    00358      throw index_invalid(*this);             // no painter with this index -> throw
    00359 }
    00360
    00361 // finds a painter_info by index
    00362 background_wnd::painter_info& background_wnd::find_painter_info(unsigned idx) const
    00363 {
    00364      iterator it = find_painter_info_iterator(idx);
    00365      return *( (*it).get() );
    00366 }
    00367
    00368 // creates the memory dc
    00369 void background_wnd::create_mem_dc(HDC hDC)
    00370 {
    00371      if (mem_dc_ == NULL) {
    00372         rectangle rc = window_rect(rel_to_self);
    00373           COLORREF crOld = SetBkColor(hDC, cr_bkgnd_); // set background color so the memdc is filled with it ;-)
    00374           mem_dc_.create_dc(hDC, rc.width(), rc.height());
    00375           SetBkColor(hDC, crOld);
    00376
    00377         // draw memory dc
    00378         redraw_mem_dc();
    00379     }
    00380 }
    00381
    00382 // returns the actual rectangle in which a painter is drawn. if the drawing rect has a width or height of 0
    00383 // or less the painter will be drawn in full client-rectangle size
    00384 rectangle background_wnd::get_actual_drawing_rect(PainterInfoPtr pInfo) const
    00385 {
    00386      // FIX_3
    00387      // If drawing rect width/height is less than 0 now the width or the height are
    00388      // separately scaled to client width/height
    00389
    00390      // if drawing-rect is null take client-rect
    00391      rectangle rc_painter = pInfo->rc_drawing_;
    00392      rectangle rc_window = window_rect(rel_to_self);
    00393      if (rc_painter.width() <= 0)
    00394           rc_painter.right = rc_window.width();
    00395      if (rc_painter.height() <= 0)
    00396           rc_painter.bottom = rc_window.height();
    00397
    00398      return rc_painter;
    00399 }
    00400
    00401 // determines if rc is totally overlapped by the rectangles stored in rects. it would be possible to use
    00402 // regions instead of this function but they're very slow
    00403 bool background_wnd::is_overlapped(const rectangle& rc, const std::vector<rectangle>& rects)
    00404 {
    00405      typedef std::vector<rectangle> RectangleCont;
    00406      RectangleCont intersecting;
    00407      unsigned area_rect = rc.height() * rc.width();
    00408      unsigned area_total = 0;
    00409
    00410      // walk through all rectangles and accumulate the area intersecting rc
    00411      for (RectangleCont::const_iterator it_added = rects.begin(); it_added != rects.end(); ++it_added) {
    00412           rectangle  rc_intersect;
    00413           if ( IntersectRect(&rc_intersect, &rc, &(*it_added)) ) {
    00414
    00415                // because more than one added rectangles can overlap themselves we have to take care of that...
    00416                for (RectangleCont::const_iterator it_intersected = intersecting.begin(); it_intersected != intersecting.end(); ++it_intersected) {
    00417                     rectangle rc_temp;
    00418                     if ( IntersectRect(&rc_temp, &rc_intersect, &(*it_intersected)) ) {
    00419
    00420                          // if the intersection isn't big enough for SubtractRect() to succeed subtract the area of the
    00421                          // intersection from area_total!
    00422                          const int width = rc_temp.width();
    00423                          const int height = rc_temp.height();
    00424                          if (width < rc_intersect.width() && height < rc_intersect.height())
    00425                               area_total -= width * height;
    00426                          else
    00427                               SubtractRect( &rc_intersect, &rc_intersect, &(*it_intersected) );
    00428                     }
    00429                }
    00430
    00431                const int width = rc_intersect.width();
    00432                const int height = rc_intersect.height();
    00433                if (width && height) {
    00434                     intersecting.push_back(rc_intersect);
    00435                     area_total += rc_intersect.width() * rc_intersect.height();
    00436
    00437                     // early return?
    00438                     if (area_total >= area_rect)
    00439                          return true;
    00440                }
    00441           }
    00442      }
    00443      return area_total >= area_rect;
    00444 }
    00445
    00446 // determines if a painter is partially visible. necessary for avoiding unnecessary redrawing
    00447 bool background_wnd::is_partially_visible(const_iterator it_info, const rectangle& rc_painter) const
    00448 {
    00449      // if painter invisible return immediately
    00450      if ( (*it_info)->bVisible_ == false || (*it_info)->transparency_ == 0)
    00451           return false;
    00452
    00453      // test if this painter is overlapped by another painter which is
    00454      //        - higher in the z-order (for-loop)
    00455      //        - has a bigger drawing_rect than this painter
    00456      //        - visible (bVisible_ set to true)
    00457      //        - opaque (transparency_ set to 255 and painter_base::is_opaque() returns true)
    00458      //
    00459      // note: it is possible that some painters TOGETHER overlap this painter...is_overlapped() checks this
    00460      std::vector<rectangle> rects_accumulated;
    00461      for (const_iterator it = it_info + 1; it != coll_painters_.end(); ++it) {
    00462
    00463           if ( (*it_info)->bVisible_ == true && (*it)->transparency_ == 255 && (*it)->get_painter().is_opaque(rc_painter.width(), rc_painter.height()) ) {
    00464                rectangle rc = get_actual_drawing_rect(*it);
    00465                ++rc.right; ++rc.bottom;           // increase by 1 because bottom right is excluded by contains_point()...
    00466                if ( rc.contains_point(rc_painter.top_left()) && rc.contains_point(rc_painter.bottom_right()) )
    00467                     return false;
    00468                else {
    00469                     --rc.right; --rc.bottom;      // reset rc
    00470                     rects_accumulated.push_back(rc);
    00471                }
    00472           }
    00473      }
    00474
    00475      // no painter overlapped overlapped the passed painter by itself...so call is_overlapped() for the accumulated
    00476      // painter drawing-rects
    00477      return is_overlapped(rc_painter, rects_accumulated) == false;
    00478 }
    00479
    00480 // redraws the memory dc
    00481 void background_wnd::redraw_mem_dc()
    00482 {
    00483     // Draw painters
    00484      for (iterator it = coll_painters_.begin(); it != coll_painters_.end(); ++it) {
    00485           rectangle rc_painter = get_actual_drawing_rect(*it);
    00486
    00487           // only draw the painter if it is partially visible...
    00488           if (is_partially_visible(it, rc_painter))
    00489                redraw_one_painter(*it, rc_painter);
    00490      }
    00491 }
    00492
    00493 // Draws a painter on the memory DC
    00494 void background_wnd::redraw_one_painter(PainterInfoPtr pInfo, const rectangle& rc_painter)
    00495 {
    00496      const int x = rc_painter.left;          const int y = rc_painter.top;
    00497      const int cx = rc_painter.width(); const int cy = rc_painter.height();
    00498
    00499      // FIX_2
    00500      // because we now have a separate DC for the painter, it can't paint outside
    00501      // its drawing-rect (before this fix the painter drawed directly on the memory
    00502      // DC...). so the clipping works now correct! this change also affects the interface
    00503      // of painter_base: the draw()-function now doesn't need the x- and y-parameters - they're
    00504      // always 0!
    00505      bg_wnd::memory_dc hPainterDC;
    00506      hPainterDC.create_dc(get_mem_dc(), cx, cy);
    00507
    00508      // FIX_1
    00509      // if a painter was transparent and not all of the area of the drawing rect is drawn
    00510      // (-> transparent) it would be filled with the background-color of hTransparentDC.
    00511      // because it is transparent you would see a colored glow...
    00512      get_mem_dc().bitblt(hPainterDC, 0, 0, cx, cy, x, y);
    00513
    00514      // FIX_2
    00515      // draw painter to painter DC
    00516      pInfo->get_painter().draw( hPainterDC, cx, cy );
    00517
    00518      // painter is opaque so bitblt from the painter DC to the memory DC
    00519      if (pInfo->transparency_ == 255) {
    00520           hPainterDC.bitblt(get_mem_dc(), x, y, cx, cy);
    00521      }
    00522      // painter is transparent so call AlphaBlend()
    00523      else {
    00524           BLENDFUNCTION bf;
    00525           bf.BlendOp = AC_SRC_OVER;
    00526           bf.BlendFlags = NULL;
    00527           bf.SourceConstantAlpha = pInfo->transparency_;
    00528           bf.AlphaFormat = 0;                     // ignore the alpha channel
    00529           AlphaBlend(get_mem_dc(), x, y, cx, cy, hPainterDC, 0, 0, cx, cy, bf);
    00530      }
    00531 }
    00532
    00533
    00534 // ============================================================================================================= //
    00535 // Handler for background_wnd
    00536
    00537 struct background_wnd_handler : event_handler<background_wnd_handler, background_wnd>
    00538 {
    00539     handle_event on_erase_bkgnd(w_param<HDC> hDC, result res)
    00540     {
    00541         // first make sure the memory DC is created
    00542         window()->create_mem_dc(hDC);
    00543           rectangle rc = window()->window_rect(rel_to_self);
    00544           window()->mem_dc_.bitblt(hDC, 0, 0, rc.width(), rc.height());
    00545
    00546         res = true;
    00547         return event<WM_ERASEBKGND> ().HANDLED_BY(&me::on_erase_bkgnd);
    00548     }
    00549
    00550     handle_event on_size(l_param_lo cx, l_param_hi cy)
    00551     {
    00552           if (cx > 0 && cy > 0 && window()->mem_dc_) {
    00553                SIZE size = window()->mem_dc_.size();
    00554
    00555                // If
    00556                // (1) the window's size increases
    00557                // (2) the window's size is FACTOR times smaller than the memory DC's
    00558                // -> delete the memory DC, so it will be recreated in on_erase_bkgnd()
    00559                const int FACTOR = 2;
    00560                if (size.cx < cx || size.cy < cy || cx * FACTOR < size.cx || cy * FACTOR < size.cy)
    00561                     window()->mem_dc_.delete_dc();
    00562           }
    00563         return event<WM_SIZE> ().HANDLED_BY(&me::on_size);
    00564     }
    00565 };
    00566
    00567
    00568 } }  // namespace win32::gui