"Fossies" - the Fresh Open Source Software Archive

Member "google-gadgets-for-linux-0.11.2/ggadget/display_window.cc" (20 Mar 2009, 19280 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 "display_window.h"
   18 #include "basic_element.h"
   19 #include "button_element.h"
   20 #include "checkbox_element.h"
   21 #include "combobox_element.h"
   22 #include "div_element.h"
   23 #include "edit_element_base.h"
   24 #include "elements.h"
   25 #include "item_element.h"
   26 #include "label_element.h"
   27 #include "listbox_element.h"
   28 #include "logger.h"
   29 #include "scriptable_array.h"
   30 #include "scriptable_event.h"
   31 #include "string_utils.h"
   32 #include "text_frame.h"
   33 #include "view.h"
   34 #include "small_object.h"
   35 
   36 namespace ggadget {
   37 
   38 static const int kLabelTextSize = -1;  // Default size.
   39 static const int kListItemHeight = 17;
   40 static const char *kControlBorderColor = "#A0A0A0";
   41 static const char *kBackgroundColor = "#FFFFFF";
   42 static const int kMinComboBoxHeight = 80;
   43 static const int kMaxComboBoxHeight = 150;
   44 
   45 class DisplayWindow::Impl : public SmallObject<> {
   46  public:
   47   enum ButtonId {
   48     ID_OK = 1,
   49     ID_CANCEL = 2
   50   };
   51 
   52   enum ControlClass {
   53     CLASS_LABEL = 0,
   54     CLASS_EDIT = 1,
   55     CLASS_LIST = 2,
   56     CLASS_BUTTON = 3
   57   };
   58 
   59   enum ControlType {
   60     TYPE_NONE = 0,
   61     TYPE_LIST_OPEN = 0,
   62     TYPE_LIST_DROP = 1,
   63     TYPE_BUTTON_PUSH = 2,
   64     TYPE_BUTTON_CHECK = 3,
   65     TYPE_EDIT_PASSWORD = 10,
   66   };
   67 
   68   class Control : public ScriptableHelperNativeOwnedDefault {
   69    public:
   70     DEFINE_CLASS_ID(0x811cc6d8013643f4, ScriptableInterface);
   71 
   72     Control(DisplayWindow *window, BasicElement *element)
   73         : window_(window), element_(element),
   74           checkbox_clicked_(false) {
   75     }
   76 
   77     virtual void DoClassRegister() {
   78       // Incompatibility: we don't allow chaning id of a control.
   79       RegisterProperty("id",
   80                        NewSlot(&BasicElement::GetName, &Control::element_),
   81                        NULL);
   82       RegisterProperty("enabled",
   83                        NewSlot(&BasicElement::IsEnabled, &Control::element_),
   84                        NewSlot(&Control::SetEnabled));
   85       RegisterProperty("text",
   86                        NewSlot(&Control::GetText),
   87                        NewSlot(&Control::SetText));
   88       RegisterProperty("value",
   89                        NewSlot(&Control::GetValue),
   90                        NewSlot(&Control::SetValue));
   91       RegisterProperty("x", NULL, NewSlot(DummySetter));
   92       RegisterProperty("y", NULL, NewSlot(DummySetter));
   93       RegisterProperty("width", NULL, NewSlot(DummySetter));
   94       RegisterProperty("height", NULL, NewSlot(DummySetter));
   95       RegisterClassSignal("onChanged", &Control::onchanged_signal_);
   96       RegisterClassSignal("onClicked", &Control::onclicked_signal_);
   97     }
   98 
   99     ScriptableArray *GetListBoxItems(ListBoxElement *listbox) {
  100       ScriptableArray *array = new ScriptableArray();
  101       size_t count = listbox->GetChildren()->GetCount();
  102       for (size_t i = 0; i < count; ++i) {
  103         BasicElement *item = listbox->GetChildren()->GetItemByIndex(i);
  104         if (item->IsInstanceOf(ItemElement::CLASS_ID))
  105           array->Append(Variant(down_cast<ItemElement*>(item)->GetLabelText()));
  106       }
  107       return array;
  108     }
  109 
  110     void SetEnabled(bool enabled) {
  111       element_->SetEnabled(enabled);
  112       element_->SetOpacity(enabled ? 1.0 : 0.7);
  113     }
  114 
  115     // Gets the full content of the control.
  116     Variant GetText() {
  117       if (element_->IsInstanceOf(ButtonElement::CLASS_ID)) {
  118         ButtonElement *button = down_cast<ButtonElement *>(element_);
  119         return Variant(button->GetTextFrame()->GetText());
  120       }
  121       if (element_->IsInstanceOf(CheckBoxElement::CLASS_ID)) {
  122         CheckBoxElement *checkbox = down_cast<CheckBoxElement *>(element_);
  123         return Variant(checkbox->GetTextFrame()->GetText());
  124       }
  125       if (element_->IsInstanceOf(LabelElement::CLASS_ID)) {
  126         LabelElement *label = down_cast<LabelElement *>(element_);
  127         return Variant(label->GetTextFrame()->GetText());
  128       }
  129       if (element_->IsInstanceOf(ListBoxElement::CLASS_ID)) {
  130         ListBoxElement *listbox = down_cast<ListBoxElement *>(element_);
  131         return Variant(GetListBoxItems(listbox));
  132       }
  133       if (element_->IsInstanceOf(ComboBoxElement::CLASS_ID)) {
  134         ComboBoxElement *combobox = down_cast<ComboBoxElement *>(element_);
  135         return Variant(GetListBoxItems(combobox->GetDroplist()));
  136       }
  137       if (element_->IsInstanceOf(EditElementBase::CLASS_ID)) {
  138         EditElementBase *edit = down_cast<EditElementBase *>(element_);
  139         return Variant(edit->GetValue());
  140       }
  141       ASSERT(false);
  142       return Variant();
  143     }
  144 
  145     static const int kMaxListItems = 512;
  146     void SetListBoxItems(ListBoxElement *listbox, ScriptableInterface *array) {
  147       listbox->GetChildren()->RemoveAllElements();
  148       if (array) {
  149         int length;
  150         if (array->GetProperty("length").v().ConvertToInt(&length)) {
  151           if (length > kMaxListItems)
  152             length = kMaxListItems;
  153           for (int i = 0; i < length; i++) {
  154             ResultVariant v = array->GetPropertyByIndex(i);
  155             std::string str_value;
  156             if (v.v().ConvertToString(&str_value)) {
  157               listbox->AppendString(str_value.c_str());
  158             } else {
  159               LOG("Invalid type of array item(%s) for control %s",
  160                   v.v().Print().c_str(), element_->GetName().c_str());
  161             }
  162           }
  163         }
  164       }
  165     }
  166 
  167     void SetText(const Variant &text) {
  168       bool invalid = false;
  169       if (text.type() == Variant::TYPE_SCRIPTABLE) {
  170         ScriptableInterface *array =
  171              VariantValue<ScriptableInterface *>()(text);
  172         if (array) {
  173           if (element_->IsInstanceOf(ListBoxElement::CLASS_ID)) {
  174             SetListBoxItems(down_cast<ListBoxElement *>(element_), array);
  175           } else if (element_->IsInstanceOf(ComboBoxElement::CLASS_ID)) {
  176             SetListBoxItems(
  177                 down_cast<ComboBoxElement *>(element_)->GetDroplist(), array);
  178           } else {
  179             invalid = true;
  180           }
  181         }
  182       } else {
  183         std::string text_str;
  184         if (text.ConvertToString(&text_str)) {
  185           if (element_->IsInstanceOf(EditElementBase::CLASS_ID)) {
  186             EditElementBase *edit = down_cast<EditElementBase *>(element_);
  187             edit->SetValue(text_str.c_str());
  188           } else {
  189             // Erase hotkey indicator in the string.
  190             size_t pos = text_str.find('&');
  191             if (pos != text_str.npos)
  192               text_str.erase(pos, 1);
  193             if (element_->IsInstanceOf(ButtonElement::CLASS_ID)) {
  194               ButtonElement *button = down_cast<ButtonElement *>(element_);
  195               button->GetTextFrame()->SetText(text_str.c_str());
  196             } else if (element_->IsInstanceOf(CheckBoxElement::CLASS_ID)) {
  197               CheckBoxElement *checkbox =
  198                   down_cast<CheckBoxElement *>(element_);
  199               checkbox->GetTextFrame()->SetText(text_str.c_str());
  200             } else if (element_->IsInstanceOf(LabelElement::CLASS_ID)) {
  201               LabelElement *label = down_cast<LabelElement *>(element_);
  202               TextFrame *text_frame = label->GetTextFrame();
  203               text_frame->SetText(text_str.c_str());
  204 
  205               text_frame->SetSize(kLabelTextSize);
  206               // Shrink the font size if the given rect can't enclose the text.
  207               double text_width, text_height;
  208               text_frame->GetExtents(element_->GetPixelWidth(),
  209                                      &text_width, &text_height);
  210               if (text_height > element_->GetPixelHeight())
  211                 text_frame->SetSize(kLabelTextSize - 1);
  212             } else if (element_->IsInstanceOf(EditElementBase::CLASS_ID)) {
  213               EditElementBase *edit = down_cast<EditElementBase *>(element_);
  214               edit->SetValue(text_str.c_str());
  215             } else {
  216               invalid = true;
  217             }
  218           }
  219         } else {
  220           invalid = true;
  221         }
  222       }
  223 
  224       if (invalid) {
  225         LOG("Invalid type of text(%s) for control %s",
  226             text.Print().c_str(), element_->GetName().c_str());
  227       }
  228     }
  229 
  230     std::string GetListBoxValue(ListBoxElement *listbox) {
  231       ItemElement *item = listbox->GetSelectedItem();
  232       return item ? item->GetLabelText() : std::string();
  233     }
  234 
  235     // The current value of the control.
  236     Variant GetValue() {
  237       if (element_->IsInstanceOf(CheckBoxElement::CLASS_ID)) {
  238         // For check box it is a boolean idicating the check state.
  239         CheckBoxElement *checkbox = down_cast<CheckBoxElement *>(element_);
  240         return Variant(checkbox->GetValue());
  241       }
  242       if (element_->IsInstanceOf(ListBoxElement::CLASS_ID)) {
  243         ListBoxElement *listbox = down_cast<ListBoxElement *>(element_);
  244         return Variant(GetListBoxValue(listbox));
  245       }
  246       if (element_->IsInstanceOf(ComboBoxElement::CLASS_ID)) {
  247         ComboBoxElement *combobox = down_cast<ComboBoxElement *>(element_);
  248         return Variant(GetListBoxValue(combobox->GetDroplist()));
  249       }
  250       // For others it is the displayed text.
  251       return GetText();
  252     }
  253 
  254     bool SetListBoxValue(ListBoxElement *listbox, const Variant &value) {
  255       std::string value_str;
  256       if (value.ConvertToString(&value_str)) {
  257         ItemElement *item = listbox->FindItemByString(value_str.c_str());
  258         if (item)
  259           listbox->SetSelectedItem(item);
  260         return true;
  261       }
  262       return false;
  263     }
  264 
  265     void SetValue(const Variant &value) {
  266       bool valid = true;
  267       if (element_->IsInstanceOf(LabelElement::CLASS_ID) ||
  268           element_->IsInstanceOf(EditElementBase::CLASS_ID)) {
  269         SetText(value);
  270       } else if (element_->IsInstanceOf(ListBoxElement::CLASS_ID)) {
  271         valid = SetListBoxValue(down_cast<ListBoxElement *>(element_), value);
  272       } else if (element_->IsInstanceOf(ComboBoxElement::CLASS_ID)) {
  273         ComboBoxElement *combobox = down_cast<ComboBoxElement *>(element_);
  274         valid = SetListBoxValue(combobox->GetDroplist(), value);
  275       } else if (element_->IsInstanceOf(CheckBoxElement::CLASS_ID)) {
  276         bool value_bool;
  277         if (value.ConvertToBool(&value_bool)) {
  278           CheckBoxElement *checkbox = down_cast<CheckBoxElement *>(element_);
  279           checkbox->SetValue(value_bool);
  280         } else {
  281           valid = false;
  282         }
  283       } else {
  284         valid = false;
  285       }
  286       if (!valid) {
  287         LOG("Invalid type of value(%s) for control %s",
  288             value.Print().c_str(), element_->GetName().c_str());
  289       }
  290     }
  291 
  292     void SetRect(int x, int y, int width, int height) {
  293       element_->SetPixelX(x);
  294       element_->SetPixelY(y);
  295       element_->SetPixelWidth(width);
  296       element_->SetPixelHeight(height);
  297     }
  298 
  299     void OnChange() {
  300       onchanged_signal_(window_, this);
  301     }
  302 
  303     void OnClick() {
  304       onclicked_signal_(window_, this);
  305     }
  306 
  307     void OnCheckBoxClick() {
  308       checkbox_clicked_ = true;
  309     }
  310 
  311     void OnCheckBoxChange() {
  312       if (checkbox_clicked_) {
  313         onclicked_signal_(window_, this);
  314         checkbox_clicked_ = false;
  315       }
  316     }
  317 
  318     void OnSize(DivElement *div) {
  319       div->SetPixelWidth(element_->GetPixelWidth() + 2);
  320       div->SetPixelHeight(element_->GetPixelHeight() + 2);
  321     }
  322 
  323     DisplayWindow *window_;
  324     BasicElement *element_;
  325     bool checkbox_clicked_;
  326     Signal2<void, DisplayWindow *, Control *> onchanged_signal_;
  327     Signal2<void, DisplayWindow *, Control *> onclicked_signal_;
  328   };
  329 
  330   Impl(DisplayWindow *owner, View *view)
  331       : owner_(owner), view_(view),
  332         min_x_(INT_MAX), min_y_(INT_MAX), max_x_(0), max_y_(0) {
  333   }
  334 
  335   ~Impl() {
  336     for (ControlsMap::iterator it = controls_.begin();
  337          it != controls_.end(); ++it) {
  338       delete it->second;
  339     }
  340     controls_.clear();
  341   }
  342 
  343   DivElement *CreateFrameDiv(Elements *elements) {
  344     DivElement *div = down_cast<DivElement *>(elements->AppendElement("div",
  345                                                                       NULL));
  346     div->SetBackground(Variant(kControlBorderColor));
  347     return div;
  348   }
  349 
  350   Control *AddControl(ControlClass ctrl_class, ControlType ctrl_type,
  351                       const char *ctrl_id, const Variant &text,
  352                       int x, int y, int width, int height) {
  353     Control *control = NULL;
  354     Elements *elements = view_->GetChildren();
  355     DivElement *div = NULL;  // Some elements may need a frame.
  356 
  357     switch (ctrl_class) {
  358       case CLASS_LABEL: {
  359         LabelElement *element = down_cast<LabelElement *>(
  360             elements->AppendElement("label", ctrl_id));
  361         TextFrame *text_frame = element->GetTextFrame();
  362         text_frame->SetWordWrap(true);
  363         text_frame->SetSize(kLabelTextSize);
  364         control = new Control(owner_, element);
  365         break;
  366       }
  367       case CLASS_EDIT: {
  368         // Our border is thinner than Windows version, so thrink the height.
  369         y += 1;
  370         height -= 2;
  371         div = CreateFrameDiv(elements);
  372         EditElementBase *element = down_cast<EditElementBase *>(
  373             elements->AppendElement("edit", ctrl_id));
  374         if (ctrl_type == TYPE_EDIT_PASSWORD)
  375           element->SetPasswordChar("*");
  376         control = new Control(owner_, element);
  377         element->ConnectOnChangeEvent(NewSlot(control, &Control::OnChange));
  378         break;
  379       }
  380       case CLASS_LIST:
  381         switch (ctrl_type) {
  382           default:
  383           case TYPE_LIST_OPEN: {
  384             div = CreateFrameDiv(elements);
  385             ListBoxElement *element = down_cast<ListBoxElement *>(
  386                 elements->AppendElement("listbox", ctrl_id));
  387             element->SetItemWidth(Variant("100%"));
  388             element->SetItemHeight(Variant(kListItemHeight));
  389             element->SetAutoscroll(true);
  390             element->SetBackground(Variant(kBackgroundColor));
  391             control = new Control(owner_, element);
  392             element->ConnectOnChangeEvent(NewSlot(control, &Control::OnChange));
  393             break;
  394           }
  395           case TYPE_LIST_DROP: {
  396             div = CreateFrameDiv(elements);
  397             ComboBoxElement *element = down_cast<ComboBoxElement *>(
  398                 elements->AppendElement("combobox", ctrl_id));
  399             element->SetType(ComboBoxElement::COMBO_DROPLIST);
  400             element->GetDroplist()->SetItemWidth(Variant("100%"));
  401             element->GetDroplist()->SetItemHeight(Variant(kListItemHeight));
  402             element->SetBackground(Variant(kBackgroundColor));
  403             control = new Control(owner_, element);
  404             element->ConnectOnChangeEvent(NewSlot(control, &Control::OnChange));
  405             // Because our combobox can't pop out of the dialog box, we must
  406             // limit the height of the combobox
  407             height = std::max(std::min(height, kMaxComboBoxHeight),
  408                               kMinComboBoxHeight);
  409             break;
  410           }
  411         }
  412         break;
  413       case CLASS_BUTTON:
  414         switch (ctrl_type) {
  415           default:
  416           case TYPE_BUTTON_PUSH: {
  417             ButtonElement *element = down_cast<ButtonElement *>(
  418                 elements->AppendElement("button", ctrl_id));
  419             element->SetDefaultRendering(true);
  420             element->GetTextFrame()->SetSize(kLabelTextSize);
  421             control = new Control(owner_, element);
  422             element->ConnectOnClickEvent(NewSlot(control, &Control::OnClick));
  423             break;
  424           }
  425           case TYPE_BUTTON_CHECK: {
  426             CheckBoxElement *element = down_cast<CheckBoxElement *>(
  427                 elements->AppendElement("checkbox", ctrl_id));
  428             element->SetDefaultRendering(true);
  429             element->GetTextFrame()->SetSize(kLabelTextSize);
  430             // Default value of gadget checkbox element is false, but here
  431             // the default value should be false.
  432             element->SetValue(false);
  433             // The DisplayWindow expects the control has changed its value when
  434             // onClicked is fired, but our CheckBoxElement changes value after
  435             // "onclick", so the control must listen to the "onchange" event,
  436             // and check whether the change is caused by click or the program.
  437             control = new Control(owner_, element);
  438             element->ConnectOnClickEvent(NewSlot(control,
  439                                                  &Control::OnCheckBoxClick));
  440             element->ConnectOnChangeEvent(NewSlot(control,
  441                                                   &Control::OnCheckBoxChange));
  442             break;
  443           }
  444         }
  445         break;
  446       default:
  447         LOG("Unknown control class: %d", ctrl_class);
  448         break;
  449     }
  450 
  451     if (control) {
  452       if (div) {
  453         div->SetPixelX(x - 1);
  454         div->SetPixelY(y - 1);
  455         control->element_->ConnectOnSizeEvent(NewSlot(control,
  456                                                       &Control::OnSize, div));
  457       }
  458       control->SetRect(x, y, width, height);
  459       control->SetText(text);
  460       min_x_ = std::min(std::max(0, x), min_x_);
  461       min_y_ = std::min(std::max(0, y), min_y_);
  462       max_x_ = std::max(x + width, max_x_);
  463       max_y_ = std::max(y + height, max_y_);
  464 
  465       controls_.insert(make_pair(std::string(ctrl_id), control));
  466       return control;
  467     }
  468 
  469     return NULL;
  470   }
  471 
  472   Control *GetControl(const char *ctrl_id) {
  473     ControlsMap::iterator it = controls_.find(ctrl_id);
  474     return it == controls_.end() ? NULL : it->second;
  475   }
  476 
  477   void OnOk() {
  478     onclose_signal_(owner_, ID_OK);
  479   }
  480 
  481   void OnCancel() {
  482     onclose_signal_(owner_, ID_CANCEL);
  483   }
  484 
  485   DisplayWindow *owner_;
  486   View *view_;
  487   Signal2<void, DisplayWindow *, ButtonId> onclose_signal_;
  488   int min_x_, min_y_, max_x_, max_y_;
  489   typedef LightMultiMap<std::string, Control *,
  490                         GadgetStringComparator> ControlsMap;
  491   ControlsMap controls_;
  492 };
  493 
  494 DisplayWindow::DisplayWindow(View *view)
  495     : impl_(new Impl(this, view)) {
  496   ASSERT(view);
  497   view->ConnectOnOkEvent(NewSlot(impl_, &Impl::OnOk));
  498   view->ConnectOnCancelEvent(NewSlot(impl_, &Impl::OnCancel));
  499 }
  500 
  501 void DisplayWindow::DoClassRegister() {
  502   RegisterMethod("AddControl", NewSlot(&Impl::AddControl,
  503                                        &DisplayWindow::impl_));
  504   RegisterMethod("GetControl", NewSlot(&Impl::GetControl,
  505                                        &DisplayWindow::impl_));
  506   RegisterClassSignal("OnClose", &Impl::onclose_signal_, &DisplayWindow::impl_);
  507 }
  508 
  509 DisplayWindow::~DisplayWindow() {
  510   delete impl_;
  511   impl_ = NULL;
  512 }
  513 
  514 bool DisplayWindow::AdjustSize() {
  515   if (impl_->min_x_ != INT_MAX && impl_->min_y_ != INT_MAX) {
  516     // Add min_x_ and min_y_ to max_x_ and max_y_ to leave equal blank areas
  517     // along the four edges.
  518     impl_->view_->SetSize(impl_->max_x_ + impl_->min_x_,
  519                           impl_->max_y_ + impl_->min_y_);
  520     return true;
  521   }
  522   return false;
  523 }
  524 
  525 } // namespace ggadget