a9a50099创建于 2012年6月14日历史提交
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_CANDIDATE_WINDOW_VIEW_H_
#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_CANDIDATE_WINDOW_VIEW_H_

#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer.h"
#include "chrome/browser/chromeos/input_method/ibus_ui_controller.h"
#include "ui/views/view.h"

namespace gfx {
class Font;
}

namespace chromeos {
namespace input_method {

class CandidateView;
class InfolistView;
class InformationTextArea;
class HidableArea;

// CandidateWindowView is the main container of the candidate window UI.
class CandidateWindowView : public views::View {
 public:
  // The object can be monitored by the observer.
  class Observer {
   public:
    virtual ~Observer() {}
    // The function is called when a candidate is committed.
    // See comments at NotifyCandidateClicked() in chromeos_input_method_ui.h
    // for details about the parameters.
    virtual void OnCandidateCommitted(int index, int button, int flag) = 0;

    virtual void OnCandidateWindowOpened() = 0;
    virtual void OnCandidateWindowClosed() = 0;
  };

  explicit CandidateWindowView(views::Widget* parent_frame);
  virtual ~CandidateWindowView();
  void Init();

  // Adds the given observer. The ownership is not transferred.
  void AddObserver(Observer* observer) {
    observers_.AddObserver(observer);
  }

  // Removes the given observer.
  void RemoveObserver(Observer* observer) {
    observers_.RemoveObserver(observer);
  }

  // Selects the candidate specified by the index in the current page
  // (zero-origin).  Changes the appearance of the selected candidate,
  // updates the information in the candidate window as needed.
  void SelectCandidateAt(int index_in_page);

  // The function is called when a candidate is being dragged. From the
  // given point, locates the candidate under the mouse cursor, and
  // selects it.
  void OnCandidatePressed(const gfx::Point& point);

  // Commits the candidate currently being selected.
  void CommitCandidate();

  // Hides the lookup table.
  void HideLookupTable();

  // Hides the auxiliary text.
  void HideAuxiliaryText();

  // Hides the preedit text.
  void HidePreeditText();

  // Hides whole the candidate window.
  void HideAll();

  // Shows the lookup table.
  void ShowLookupTable();

  // Shows the auxiliary text.
  void ShowAuxiliaryText();

  // Shows the preedit text.
  void ShowPreeditText();

  // Updates the auxiliary text.
  void UpdateAuxiliaryText(const std::string& utf8_text);

  // Updates the preedit text.
  void UpdatePreeditText(const std::string& utf8_text);

  // Returns true if we should update candidate views in the window.  For
  // instance, if we are going to show the same candidates as before, we
  // don't have to update candidate views. This happens when the user just
  // moves the cursor in the same page in the candidate window.
  static bool ShouldUpdateCandidateViews(
      const InputMethodLookupTable& old_table,
      const InputMethodLookupTable& new_table);

  // Updates candidates of the candidate window from |lookup_table|.
  // Candidates are arranged per |orientation|.
  void UpdateCandidates(const InputMethodLookupTable& lookup_table);

  // Resizes and moves the parent frame. The two actions should be
  // performed consecutively as resizing may require the candidate window
  // to move. For instance, we may need to move the candidate window from
  // below the cursor to above the cursor, if the candidate window becomes
  // too big to be shown near the bottom of the screen.  This function
  // needs to be called when the visible contents of the candidate window
  // are modified.
  void ResizeAndMoveParentFrame();

  // Returns the horizontal offset used for placing the vertical candidate
  // window so that the first candidate is aligned with the the text being
  // converted like:
  //
  //      XXX           <- The user is converting XXX
  //   +-----+
  //   |1 XXX|
  //   |2 YYY|
  //   |3 ZZZ|
  //
  // Returns 0 if no candidate is present.
  int GetHorizontalOffset();

  void set_cursor_location(const gfx::Rect& cursor_location) {
    cursor_location_ = cursor_location;
  }

  void set_composition_head_location(
      const gfx::Rect& composition_head_location) {
    composition_head_location_ = composition_head_location;
  }

  const gfx::Rect& cursor_location() const { return cursor_location_; }
  const gfx::Rect& composition_head_location() const {
    return composition_head_location_;
  }

 protected:
  // Override View::VisibilityChanged()
  virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE;

  // Override View::OnBoundsChanged()
  virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;

 private:
  FRIEND_TEST_ALL_PREFIXES(CandidateWindowViewTest, MozcUpdateCandidateTest);
  FRIEND_TEST_ALL_PREFIXES(CandidateWindowViewTest, ShortcutSettingTest);
  FRIEND_TEST_ALL_PREFIXES(CandidateWindowViewTest,
                           DoNotChangeRowHeightWithLabelSwitchTest);

  // Initializes the candidate views if needed.
  void MaybeInitializeCandidateViews(
      const InputMethodLookupTable& lookup_table);

  // Returns the appropriate area (header or footer) to put auxiliary texts.
  InformationTextArea* GetAuxiliaryTextArea();

  // Returns true if the candidate window is open.  The suggestion window does
  // not count as the candidate window.
  bool IsCandidateWindowOpen() const;

  // Notifies observers if the candidate window's opened/closed state has
  // changed from the previous call to this function.
  void NotifyIfCandidateWindowOpenedOrClosed();

  // The lookup table (candidates).
  InputMethodLookupTable lookup_table_;

  // The index in the current page of the candidate currently being selected.
  int selected_candidate_index_in_page_;

  // The observers of the object.
  ObserverList<Observer> observers_;

  // The parent frame.
  views::Widget* parent_frame_;

  // Views created in the class will be part of tree of |this|, so these
  // child views will be deleted when |this| is deleted.

  // The preedit area is where the preedit text is shown, if it is needed
  // in cases such as the focus is on a plugin that doesn't support in-line
  // preedit drawing.
  InformationTextArea* preedit_area_;
  // The header area is where the auxiliary text is shown, if the
  // orientation is horizontal. If the auxiliary text is not provided, we
  // show nothing.  For instance, we show pinyin text like "zhong'guo".
  InformationTextArea* header_area_;
  // The candidate area is where candidates are rendered.
  HidableArea* candidate_area_;
  // The candidate views are used for rendering candidates.
  std::vector<CandidateView*> candidate_views_;
  // The footer area is where the auxiliary text is shown, if the
  // orientation is vertical. Usually the auxiliary text is used for
  // showing candidate number information like 2/19.
  InformationTextArea* footer_area_;

  // Current columns size in |candidate_area_|.
  gfx::Size previous_shortcut_column_size_;
  gfx::Size previous_candidate_column_size_;
  gfx::Size previous_annotation_column_size_;

  // The last cursor location.
  gfx::Rect cursor_location_;

  // The last compostion head location.
  gfx::Rect composition_head_location_;

  // True if the candidate window should be shown with aligning with composition
  // text as opposed to the cursor.
  bool should_show_at_composition_head_;

  // True if the candidate window should be shonw on the upper side of
  // composition text.
  bool should_show_upper_side_;

  // True if the candidate window was open.  This is used to determine when to
  // send OnCandidateWindowOpened and OnCandidateWindowClosed events.
  bool was_candidate_window_open_;

  // This function judge whether the candidate window should be shown or not,
  // if should be, shows parent_frame and if not, hides parent_frame.
  void UpdateParentArea();

  DISALLOW_COPY_AND_ASSIGN(CandidateWindowView);
};

// InfolistWindowView is the main container of the infolist window UI.
class InfolistWindowView : public views::View {
 public:
  InfolistWindowView(views::Widget* parent_frame,
                     views::Widget* candidate_window_frame);
  virtual ~InfolistWindowView();
  void Init();
  void Show();
  void DelayShow(unsigned int milliseconds);
  void Hide();
  void DelayHide(unsigned int milliseconds);
  void UpdateCandidates(const InputMethodLookupTable& lookup_table);
  void ResizeAndMoveParentFrame();
  gfx::Font GetTitleFont() const;
  gfx::Font GetDescriptionFont() const;

 protected:
  // Override View::VisibilityChanged()
  virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE;

  // Override View::OnBoundsChanged()
  virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;

 private:
  FRIEND_TEST_ALL_PREFIXES(InfolistWindowViewTest, ShouldUpdateViewTest);
  // Called by show_hide_timer_
  void OnShowHideTimer();

  // Information list
  scoped_ptr<mozc::commands::InformationList> usages_;

  // The parent frame.
  views::Widget* parent_frame_;

  // The candidate window frame.
  views::Widget* candidate_window_frame_;

  // The infolist area is where the meanings and the usages of the words are
  // rendered.
  views::View* infolist_area_;
  // The infolist views are used for rendering the meanings and the usages of
  // the words.
  std::vector<InfolistView*> infolist_views_;

  bool visible_;

  base::OneShotTimer<InfolistWindowView> show_hide_timer_;

  static bool ShouldUpdateView(
    const mozc::commands::InformationList* old_usages,
    const mozc::commands::InformationList* new_usages);

  // Information title font
  scoped_ptr<gfx::Font> title_font_;
  // Information description font
  scoped_ptr<gfx::Font> description_font_;

  DISALLOW_COPY_AND_ASSIGN(InfolistWindowView);
};

}  // namespace input_method
}  // namespace chromeos

#endif  // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_CANDIDATE_WINDOW_VIEW_H_