libyui-gtk-pkg  2.42.9
 All Classes
ygtkpkgsearchentry.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /* YGtkPkgSearchEntry, zypp query string attributes */
5 // check the header file for information about this widget
6 
7 /*
8  Textdomain "gtk"
9  */
10 
11 #include "YGi18n.h"
12 #include "config.h"
13 #include "YGUtils.h"
14 #include "ygtkpkgsearchentry.h"
15 #include "YGPackageSelector.h"
16 #include <gtk/gtk.h>
17 
19  GtkWidget *box, *entry, *combo;
20 };
21 
22 static void entry_icons_sync (GtkWidget *widget)
23 {
24  static GdkColor yellow = { 0, 0xf7f7, 0xf7f7, 0xbdbd };
25  static GdkColor black = { 0, 0, 0, 0 };
26 
27  GtkEntry *entry = GTK_ENTRY (widget); // show clear icon if text
28  const gchar *name = gtk_entry_get_text (entry);
29  bool showIcon = *name;
30  if (showIcon != gtk_entry_get_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY)) {
31  gtk_entry_set_icon_activatable (entry,
32  GTK_ENTRY_ICON_SECONDARY, showIcon);
33  gtk_entry_set_icon_from_stock (entry,
34  GTK_ENTRY_ICON_SECONDARY, showIcon ? GTK_STOCK_CLEAR : NULL);
35 
36  if (showIcon) {
37  gtk_entry_set_icon_tooltip_text (entry,
38  GTK_ENTRY_ICON_SECONDARY, _("Clear"));
39  gtk_widget_modify_base (widget, GTK_STATE_NORMAL, &yellow);
40  gtk_widget_modify_text (widget, GTK_STATE_NORMAL, &black);
41  }
42  else { // revert
43  gtk_widget_modify_base (widget, GTK_STATE_NORMAL, NULL);
44  gtk_widget_modify_text (widget, GTK_STATE_NORMAL, NULL);
45  }
46  }
47 }
48 
49 static void entry_changed_cb (GtkEditable *editable, YGtkPkgSearchEntry *pThis)
50 {
51  int item = gtk_combo_box_get_active (GTK_COMBO_BOX (pThis->impl->combo));
52  pThis->notifyDelay (item == 0 ? 150 : 500);
53  entry_icons_sync (GTK_WIDGET (editable));
54 }
55 
56 static void combo_changed_cb (GtkComboBox *combo, YGtkPkgSearchEntry *pThis)
57 {
58  const gchar *name = gtk_entry_get_text (GTK_ENTRY (pThis->impl->entry));
59  if (*name) // unless entry has text, no need to refresh query
60  pThis->notify();
61  gtk_editable_select_region (GTK_EDITABLE (pThis->impl->entry), 0, -1);
62  gtk_widget_grab_focus (pThis->impl->entry);
63 }
64 
65 static void icon_press_cb (GtkEntry *entry, GtkEntryIconPosition pos,
66  GdkEvent *event, YGtkPkgSearchEntry *pThis)
67 {
68  if (pos == GTK_ENTRY_ICON_PRIMARY)
69  gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
70  else
71  gtk_entry_set_text (entry, "");
72  gtk_widget_grab_focus (GTK_WIDGET (entry));
73 }
74 
75 static void activate_cb (GtkEntry *entry, GtkWidget *widget)
76 { gtk_widget_grab_focus (widget); }
77 
78 YGtkPkgSearchEntry::YGtkPkgSearchEntry()
79 : YGtkPkgQueryWidget(), impl (new Impl())
80 {
81  impl->entry = gtk_entry_new();
82  gtk_widget_set_size_request (impl->entry, 180, -1);
83  g_signal_connect (G_OBJECT (impl->entry), "realize", // grab focus at start
84  G_CALLBACK (gtk_widget_grab_focus), NULL);
85  g_signal_connect (G_OBJECT (impl->entry), "changed",
86  G_CALLBACK (entry_changed_cb), this);
87  g_signal_connect (G_OBJECT (impl->entry), "icon-press",
88  G_CALLBACK (icon_press_cb), this);
89  gtk_widget_set_tooltip_markup (impl->entry,
90  _("<b>Package search:</b> Use spaces to separate your keywords.\n"
91  "(usage example: a name search for \"yast dhcp\" would match yast dhcpd tool)"));
92 
93  gtk_entry_set_icon_from_stock (GTK_ENTRY (impl->entry),
94  GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
95  gtk_entry_set_icon_activatable (GTK_ENTRY (impl->entry), GTK_ENTRY_ICON_PRIMARY, TRUE);
96 
97  std::string label_str (YGUtils::mapKBAccel (_("&Find:")));
98  GtkWidget *label = gtk_label_new_with_mnemonic (label_str.c_str());
99  gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->entry);
100 
101  impl->combo = gtk_combo_box_text_new();
102  GtkComboBoxText *combot = GTK_COMBO_BOX_TEXT (impl->combo);
103  gtk_combo_box_text_append (combot, 0, _("Name & Summary"));
104  gtk_combo_box_text_append (combot, 0, _("Description"));
105  if (!YGPackageSelector::get()->onlineUpdateMode()) {
106  gtk_combo_box_text_append (combot, 0, _("File name"));
107  gtk_combo_box_text_append (combot, 0, "RPM Provides");
108  gtk_combo_box_text_append (combot, 0, "RPM Requires");
109  }
110  gtk_combo_box_set_active (GTK_COMBO_BOX (impl->combo), 0);
111  YGUtils::shrinkWidget (impl->combo);
112  g_signal_connect (G_OBJECT (impl->combo), "changed",
113  G_CALLBACK (combo_changed_cb), this);
114  gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (impl->combo),
115  YGUtils::empty_row_is_separator_cb, GINT_TO_POINTER (0), NULL);
116 
117  impl->box = gtk_hbox_new (FALSE, 3);
118  gtk_box_pack_start (GTK_BOX (impl->box), label, FALSE, TRUE, 0);
119  gtk_box_pack_start (GTK_BOX (impl->box), impl->entry, TRUE, TRUE, 0);
120  gtk_box_pack_start (GTK_BOX (impl->box), gtk_label_new (_("by")), FALSE, TRUE, 0);
121  gtk_box_pack_start (GTK_BOX (impl->box), impl->combo, TRUE, TRUE, 0);
122  gtk_widget_show_all (impl->box);
123 }
124 
125 YGtkPkgSearchEntry::~YGtkPkgSearchEntry()
126 { delete impl; }
127 
128 GtkWidget *YGtkPkgSearchEntry::getWidget()
129 { return impl->box; }
130 
131 void YGtkPkgSearchEntry::clearSelection()
132 { setText (Ypp::PoolQuery::NAME, ""); }
133 
134 bool YGtkPkgSearchEntry::writeQuery (Ypp::PoolQuery &query)
135 {
136  const gchar *text = gtk_entry_get_text (GTK_ENTRY (impl->entry));
137  if (*text) {
138  int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo));
139  if (item >= 0 && item <= 1) {
140  int attrbs;
141  switch (item) {
142  case 0: // name & summary
143  attrbs = Ypp::StrMatch::NAME | Ypp::StrMatch::SUMMARY;
144  break;
145  case 1: // description
146  attrbs = Ypp::StrMatch::DESCRIPTION;
147  break;
148  }
149 
150  Ypp::StrMatch *match = new Ypp::StrMatch (attrbs);
151  const gchar delimiter[2] = { ' ', '\0' };
152  gchar **keywords = g_strsplit (text, delimiter, -1);
153  for (gchar **i = keywords; *i; i++)
154  match->add (*i);
155  g_strfreev (keywords);
156 
157  query.addCriteria (match);
158  }
159  else {
160  query.setStringMode (false, Ypp::PoolQuery::CONTAINS);
161 
162  switch (item) {
163  case 2: // filelist
164  query.addStringAttribute (Ypp::PoolQuery::PROVIDES);
165  query.addStringAttribute (Ypp::PoolQuery::FILELIST);
166  break;
167  case 3: // provides
168  query.addStringAttribute (Ypp::PoolQuery::PROVIDES);
169  break;
170  case 4: // requires
171  query.addStringAttribute (Ypp::PoolQuery::REQUIRES);
172  break;
173  }
174 
175  query.addStringOr (text);
176  }
177  return true;
178  }
179  return false;
180 }
181 
182 static gboolean patterns_link_cb (GtkLabel *label, gchar *uri, YGtkPkgSearchEntry *pThis)
183 { YGPackageSelector::get()->showFilterWidget (uri); return TRUE; }
184 
185 static bool any_pattern_contains (const char *name)
186 {
187  if (strstr (name, " ")) return false;
188 
189  // roll on some criteria to find whole-word match (the zypp one doesnt cut it)
190  struct WholeWordMatch : public Ypp::Match {
191  WholeWordMatch (const char *word)
192  : word (word), len (strlen (word)) {}
193 
194  virtual bool match (Ypp::Selectable &sel) {
195  std::string name (sel.name());
196  const char *i = name.c_str();
197  while ((i = strcasestr (i, word))) {
198  bool starts = (i == word) || (i[-1] != ' ');
199  bool ends = (i[len+1] == '\0') || (i[len+1] != ' ');
200  if (starts && ends)
201  return true;
202  i += len;
203  }
204  return false;
205  }
206  private: const char *word; int len;
207  };
208 
209  Ypp::PoolQuery query (Ypp::Selectable::PATTERN);
210  query.addCriteria (new WholeWordMatch (name));
211  //query.addStringOr (name);
212  return query.hasNext();
213 }
214 
215 GtkWidget *YGtkPkgSearchEntry::createToolbox()
216 {
217  const gchar *text = gtk_entry_get_text (GTK_ENTRY (impl->entry));
218  int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo));
219  if (*text)
220  switch (item) {
221  case 0:
222  if (any_pattern_contains (text)) {
223  char *_text = g_strdup_printf (_("%sPatterns are available%s"
224  " that correspond to your search criteria."),
225  "<a href=\"patterns\">", "</a>");
226  GtkWidget *label = gtk_label_new (_text);
227  g_free (_text);
228  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
229  gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
230  g_signal_connect (G_OBJECT (label), "activate-link",
231  G_CALLBACK (patterns_link_cb), this);
232  GtkWidget *icon = gtk_image_new_from_stock (
233  GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
234  GtkWidget *hbox = gtk_hbox_new (FALSE, 6);
235  gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0);
236  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
237  gtk_widget_show_all (hbox);
238  return hbox;
239  }
240  break;
241  case 2: {
242  GtkWidget *label = gtk_label_new (
243  _("Search by file name only reliable for installed packages."));
244  gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
245  GtkWidget *icon = gtk_image_new_from_stock (
246  GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON);
247  GtkWidget *hbox = gtk_hbox_new (FALSE, 6);
248  gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0);
249  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
250  gtk_widget_show_all (hbox);
251  return hbox;
252  }
253  }
254  return NULL;
255 }
256 
257 void YGtkPkgSearchEntry::setText (Ypp::PoolQuery::StringAttribute attrb, const std::string &text)
258 {
259  int index = 0;
260  switch (attrb) {
261  case Ypp::PoolQuery::NAME: case Ypp::PoolQuery::SUMMARY: break;
262  case Ypp::PoolQuery::DESCRIPTION: index = 1; break;
263  case Ypp::PoolQuery::FILELIST: index = 2; break;
264  case Ypp::PoolQuery::PROVIDES: index = 3; break;
265  case Ypp::PoolQuery::REQUIRES: index = 4; break;
266  }
267 
268  g_signal_handlers_block_by_func (impl->entry, (gpointer) entry_changed_cb, this);
269  g_signal_handlers_block_by_func (impl->combo, (gpointer) combo_changed_cb, this);
270  gtk_combo_box_set_active (GTK_COMBO_BOX (impl->combo), index);
271  gtk_entry_set_text (GTK_ENTRY (impl->entry), text.c_str());
272  entry_icons_sync (impl->entry);
273  g_signal_handlers_unblock_by_func (impl->entry, (gpointer) entry_changed_cb, this);
274  g_signal_handlers_unblock_by_func (impl->combo, (gpointer) combo_changed_cb, this);
275 }
276 
277 void YGtkPkgSearchEntry::setActivateWidget (GtkWidget *widget)
278 {
279  g_signal_connect (G_OBJECT (impl->entry), "activate",
280  G_CALLBACK (activate_cb), widget);
281 }
282 
283 Ypp::PoolQuery::StringAttribute YGtkPkgSearchEntry::getAttribute()
284 {
285  int item = gtk_combo_box_get_active (GTK_COMBO_BOX (impl->combo));
286  switch (item) {
287  case 0: return Ypp::PoolQuery::NAME; break;
288  case 1: return Ypp::PoolQuery::DESCRIPTION; break;
289  case 2: return Ypp::PoolQuery::FILELIST; break;
290  case 3: return Ypp::PoolQuery::PROVIDES; break;
291  case 4: return Ypp::PoolQuery::REQUIRES; break;
292  }
293  return (Ypp::PoolQuery::StringAttribute) 0;
294 }
295 
296 std::list <std::string> YGtkPkgSearchEntry::getText()
297 {
298  std::list <std::string> keywords;
299  const char *text = gtk_entry_get_text (GTK_ENTRY (impl->entry));
300  const gchar delimiter[2] = { ' ', '\0' };
301  gchar **_keywords = g_strsplit (text, delimiter, -1);
302  for (gchar **i = _keywords; *i; i++)
303  if (**i)
304  keywords.push_back (*i);
305  g_strfreev (_keywords);
306  return keywords;
307 }
308 
309 std::string YGtkPkgSearchEntry::getTextStr()
310 { return gtk_entry_get_text (GTK_ENTRY (impl->entry)); }
311