libyui-gtk-pkg  2.42.9
 All Classes
ygtkpkghistorydialog.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /* YGtkPkgHistoryDialog, dialog */
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 "ygtkpkghistorydialog.h"
14 #include "YGDialog.h"
15 #include "YGUtils.h"
16 #include "YGPackageSelector.h"
17 #include "ygtkpkglistview.h"
18 #include <gtk/gtk.h>
19 #include "ygtktreeview.h"
20 #include "ygtkcellrenderertext.h"
21 
22 #include <zypp/parser/HistoryLogReader.h>
23 #define FILENAME "/var/log/zypp/history"
24 
25 static std::string reqbyTreatment (const std::string &reqby)
26 {
27  if (reqby.empty())
28  return _("automatic");
29  if (reqby.compare (0, 4, "root", 4) == 0) {
30  std::string str (_("user:"));
31  str += " root";
32  return str;
33  }
34  return reqby;
35 }
36 
37 struct Handler
38 {
39  virtual void date (const std::string &str, bool first) = 0;
40  virtual void item (const std::string &action, const std::string &name,
41  const std::string &description, const std::string &repositoryName,
42  const std::string &repositoryUrl, const std::string &reqby, bool autoReq) = 0;
43 };
44 
46 {
47  GtkListStore *store;
48 
49  enum Column { ICON_COLUMN, NAME_COLUMN, VERSION_URL_COLUMN, REPOSITORY_COLUMN,
50  REQBY_COLUMN, REPOSITORY_ICON_COLUMN, REPOSITORY_URL_COLUMN, SHORTCUT_COLUMN,
51  COLOR_COLUMN, XPAD_COLUMN, TOTAL_COLUMNS };
52 
54  {
55  store = gtk_list_store_new (TOTAL_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
56  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
57  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
58  }
59 
61  { g_object_unref (G_OBJECT (store)); }
62 
63  GtkTreeModel *getModel()
64  { return GTK_TREE_MODEL (store); }
65 
66  void addRow (GtkTreeIter *iter)
67  {
68  gtk_list_store_append (store, iter);
69  gtk_list_store_set (store, iter, ICON_COLUMN, NULL, NAME_COLUMN, NULL,
70  VERSION_URL_COLUMN, NULL, REPOSITORY_COLUMN, NULL, REQBY_COLUMN, NULL,
71  REPOSITORY_ICON_COLUMN, NULL, REPOSITORY_URL_COLUMN, NULL,
72  SHORTCUT_COLUMN, NULL, COLOR_COLUMN, NULL, XPAD_COLUMN, 0, -1);
73  }
74 
75  int date (const std::string &str, bool first)
76  {
77  GtkTreeIter iter;
78  if (!first) addRow (&iter); // white space
79  std::string _date = std::string ("<b>\u26ab ") + str + "</b>";
80  addRow (&iter);
81  gtk_list_store_set (store, &iter, NAME_COLUMN, _date.c_str(), -1);
82 
83  GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
84  int row = gtk_tree_path_get_indices (path)[0];
85  gtk_tree_path_free (path);
86  return row;
87  }
88 
89  void item (const std::string &action, const std::string &name,
90  const std::string &description, const std::string &repositoryName,
91  const std::string &repositoryUrl, const std::string &reqby, bool autoReq)
92  {
93  GtkTreeIter iter;
94  const char *icon = 0;
95  std::string shortcut (name);
96  if (action == _("install"))
97  icon = GTK_STOCK_ADD;
98  else if (action == _("upgrade"))
99  icon = GTK_STOCK_GO_UP;
100  else if (action == _("remove"))
101  icon = GTK_STOCK_REMOVE;
102  else if (action == _("downgrade"))
103  icon = GTK_STOCK_GO_DOWN;
104  else if (action == _("re-install"))
105  icon = GTK_STOCK_REFRESH;
106  else {
107  icon = getRepositoryStockIcon (name);
108  shortcut = "_repo";
109  }
110 
111  std::string _name;
112  _name.reserve (action.size() + name.size() + 64);
113  _name = "<b>";
114  _name += action + "</b> " + name;
115  int xpad = 0; // autoReq ? 25 : 0;
116 
117  const char *repo_icon = 0, *color = 0;
118  bool is_patch = false;
119  if (action == _("upgrade") && !repositoryUrl.empty()) {
120  // if 'upgrade' and from '*update*' server then mark as patch
121  repo_icon = getRepositoryStockIcon (repositoryUrl);
122  if (repositoryUrl.find ("update") != std::string::npos) {
123  //color = "red";
124  std::string tag;
125  tag.reserve (64);
126  tag = "<small><span color=\"#999999\">"; tag += _("patch"); tag += "</span></small>";
127  _name += " "; _name += tag;
128  is_patch = true;
129  }
130  }
131  if (autoReq && !is_patch) { // dependency
132  std::string tag;
133  tag.reserve (64);
134  tag = "<small><span color=\"#999999\">"; tag += _("auto"); tag += "</span></small>";
135  _name += " "; _name += tag;
136  }
137 
138  gtk_list_store_append (store, &iter);
139  gtk_list_store_set (store, &iter, ICON_COLUMN, icon, NAME_COLUMN, _name.c_str(),
140  VERSION_URL_COLUMN, description.c_str(), REPOSITORY_COLUMN, repositoryName.c_str(),
141  REPOSITORY_ICON_COLUMN, repo_icon, REPOSITORY_URL_COLUMN, repositoryUrl.c_str(),
142  REQBY_COLUMN, reqby.c_str(), SHORTCUT_COLUMN, shortcut.c_str(),
143  COLOR_COLUMN, color, XPAD_COLUMN, xpad, -1);
144  }
145 };
146 
148 {
149  GtkListStore *store;
150 
151  enum Column { TEXT_COLUMN, LOG_ROW_COLUMN, TOTAL_COLUMNS };
152 
154  { store = gtk_list_store_new (TOTAL_COLUMNS, G_TYPE_STRING, G_TYPE_INT); }
155 
156  ~DateListHandler()
157  { g_object_unref (G_OBJECT (store)); }
158 
159  GtkTreeModel *getModel()
160  { return GTK_TREE_MODEL (store); }
161 
162  void date (const std::string &str, bool first, int log_row)
163  {
164  GtkTreeIter iter;
165  gtk_list_store_append (store, &iter);
166  gtk_list_store_set (store, &iter, TEXT_COLUMN, str.c_str(),
167  LOG_ROW_COLUMN, log_row, -1);
168  }
169 };
170 
171 struct ListHandler : public Handler
172 {
173  LogListHandler *log_handler;
174  DateListHandler *date_handler;
175 
176  ListHandler()
177  {
178  log_handler = new LogListHandler();
179  date_handler = new DateListHandler();
180  }
181 
182  ~ListHandler()
183  { delete log_handler; delete date_handler; }
184 
185  virtual void date (const std::string &str, bool first)
186  {
187  int row = log_handler->date (str, first);
188  date_handler->date (str, first, row);
189  }
190 
191  virtual void item (const std::string &a, const std::string &n,
192  const std::string &d, const std::string &rn,
193  const std::string &ru, const std::string &rb, bool ar)
194  { log_handler->item (a, n, d, rn, ru, rb, ar); }
195 };
196 
197 struct FileHandler : public Handler
198 {
199  FILE *file;
200 
201  FileHandler (const char *filename)
202  { file = fopen (filename, "w"); }
203 
204  ~FileHandler()
205  { fclose (file); }
206 
207  void addSpace()
208  { (void) fwrite ("\n", sizeof (char), 1, file); }
209 
210  virtual void date (const std::string &str, bool first)
211  {
212  if (!first)
213  addSpace();
214  (void) fwrite (str.c_str(), sizeof (char), str.size(), file);
215  addSpace(); addSpace();
216  }
217 
218  virtual void item (const std::string &action, const std::string &name,
219  const std::string &description, const std::string &repositoryName,
220  const std::string &repositoryUrl, const std::string &reqby, bool autoReq)
221  {
222  std::string str;
223  str.reserve (action.size() + name.size() + description.size() + 4);
224  str = std::string ("\t") + action + " " + name + " " + description + "\n";
225  (void) fwrite (str.c_str(), sizeof (char), str.size(), file);
226  }
227 };
228 
230 {
231  Handler *handler;
232  std::string _date;
233  std::map <std::string, zypp::Edition> installed;
234 
235  ZyppHistoryParser (Handler *handler)
236  : handler (handler)
237  {
238  zypp::parser::HistoryLogReader parser (FILENAME, zypp::parser::HistoryLogReader::Options(), boost::ref (*this));
239  try {
240  parser.readAll();
241  }
242  catch (const zypp::Exception &ex) {
243  yuiWarning () << "Error: Could not load log file" << FILENAME << ": "
244  << ex.asUserHistory() << std::endl;
245  }
246  }
247 
248  bool operator() (const zypp::HistoryLogData::Ptr &item)
249  {
250  std::string date (item->date().form ("%d %B %Y"));
251  if (_date != date) {
252  handler->date (date, _date.empty());
253  _date = date;
254  }
255 
256  std::string action, name, descrpt, repoName, repoUrl, reqby, t;
257  bool autoreq = false;
258  switch (item->action().toEnum()) {
259  case zypp::HistoryActionID::NONE_e:
260  break;
261  case zypp::HistoryActionID::INSTALL_e: {
262  zypp::HistoryLogDataInstall *_item =
263  static_cast <zypp::HistoryLogDataInstall *> (item.get());
264  name = _item->name();
265  descrpt = _item->edition().version();
266  Ypp::getRepositoryFromAlias (_item->repoAlias(), repoName, repoUrl);
267  reqby = _item->reqby(); autoreq = reqby.empty();
268  reqby = reqbyTreatment (reqby);
269  zypp::Edition edition = _item->edition();
270  std::map <std::string, zypp::Edition>::iterator it;
271  it = installed.find (name);
272  if (it == installed.end())
273  action = _("install");
274  else {
275  zypp::Edition prev_edition = it->second;
276  if (edition > prev_edition)
277  action = _("upgrade");
278  else if (edition < prev_edition)
279  action = _("downgrade");
280  else // (edition == prev_edition)
281  action = _("re-install");
282  }
283  installed[name] = edition;
284  break;
285  }
286  case zypp::HistoryActionID::REMOVE_e: {
287  zypp::HistoryLogDataRemove *_item =
288  static_cast <zypp::HistoryLogDataRemove *> (item.get());
289  action = _("remove");
290  name = _item->name();
291  descrpt = _item->edition().version();
292  reqby = _item->reqby(); autoreq = reqby.empty();
293  reqby = reqbyTreatment (reqby);
294  std::map <std::string, zypp::Edition>::iterator it;
295  it = installed.find (name);
296  if (it != installed.end())
297  installed.erase (it);
298  break;
299  }
300  case zypp::HistoryActionID::REPO_ADD_e: {
301  zypp::HistoryLogDataRepoAdd *_item =
302  static_cast <zypp::HistoryLogDataRepoAdd *> (item.get());
303  action = _("add repository");
304  Ypp::getRepositoryFromAlias (_item->alias(), name, t);
305  descrpt = _item->url().asString();
306  break;
307  }
308  case zypp::HistoryActionID::REPO_REMOVE_e: {
309  zypp::HistoryLogDataRepoRemove *_item =
310  static_cast <zypp::HistoryLogDataRepoRemove *> (item.get());
311  action = _("remove repository");
312  name = _item->alias();
313  break;
314  }
315  case zypp::HistoryActionID::REPO_CHANGE_ALIAS_e: {
316  zypp::HistoryLogDataRepoAliasChange *_item =
317  static_cast <zypp::HistoryLogDataRepoAliasChange *> (item.get());
318  action = _("change repository alias");
319  name = _item->oldAlias() + " -> " + _item->newAlias();
320  break;
321  }
322  case zypp::HistoryActionID::REPO_CHANGE_URL_e: {
323  zypp::HistoryLogDataRepoUrlChange *_item =
324  static_cast <zypp::HistoryLogDataRepoUrlChange *> (item.get());
325  action = _("change repository url");
326  Ypp::getRepositoryFromAlias (_item->alias(), name, t);
327  descrpt = _item->newUrl().asString();
328  break;
329  }
330  }
331  if (!action.empty())
332  handler->item (action, name, descrpt, repoName, repoUrl, reqby, autoreq);
333  return true;
334  }
335 };
336 
337 static void goto_clicked (GtkTreeView *log_view)
338 {
339  GtkTreeSelection *selection = gtk_tree_view_get_selection (log_view);
340  GtkTreeModel *model;
341  GtkTreeIter iter;
342  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
343  char *shortcut;
344  gtk_tree_model_get (model, &iter, LogListHandler::SHORTCUT_COLUMN, &shortcut, -1);
345  if (!strcmp (shortcut, "_repo"))
346  YGPackageSelector::get()->showRepoManager();
347  else
348  YGPackageSelector::get()->searchFor (Ypp::PoolQuery::NAME, shortcut);
349  g_free (shortcut);
350 
351  GtkWidget *dialog = gtk_widget_get_toplevel (GTK_WIDGET (log_view));
352  gtk_widget_hide (dialog);
353  }
354 }
355 
356 static void log_selection_changed_cb (GtkTreeSelection *selection, GtkDialog *dialog)
357 {
358  bool selected = gtk_tree_selection_count_selected_rows (selection) > 0;
359  gtk_dialog_set_response_sensitive (dialog, 1, selected);
360 }
361 
362 static void log_row_activated_cb (GtkTreeView *view, GtkTreePath *path,
363  GtkTreeViewColumn *column)
364 { goto_clicked (view); }
365 
366 static gboolean log_can_select_cb (GtkTreeSelection *selection, GtkTreeModel *model,
367  GtkTreePath *path, gboolean path_currently_selected, gpointer data)
368 {
369  GtkTreeIter iter;
370  gtk_tree_model_get_iter (model, &iter, path);
371  gchar *shortcut;
372  gtk_tree_model_get (model, &iter, LogListHandler::SHORTCUT_COLUMN, &shortcut, -1);
373  bool can_select = shortcut != NULL;
374  if (shortcut) g_free (shortcut);
375  return can_select;
376 }
377 
378 static void date_selection_changed_cb (GtkTreeSelection *selection, GtkTreeView *log_view)
379 {
380  GtkTreeModel *model;
381  GtkTreeIter iter;
382  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
383  int log_row;
384  gtk_tree_model_get (model, &iter, 1, &log_row, -1);
385 
386  GtkTreePath *path = gtk_tree_path_new_from_indices (log_row, -1);
387  gtk_tree_view_scroll_to_cell (log_view, path, NULL, TRUE, 0, 0);
388  gtk_tree_path_free (path);
389  }
390 }
391 
392 static void save_to_file (GtkWindow *parent)
393 {
394  GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save to"), parent,
395  GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
396  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
397  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
398  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
399 
400  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
401  gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
402  FileHandler handler (filename);
403  ZyppHistoryParser parser (&handler);
404  g_free (filename);
405  }
406  gtk_widget_destroy (dialog);
407 }
408 
409 static void response_cb (GtkDialog *dialog, gint response, GtkTreeView *log_view)
410 {
411  switch (response) {
412  case 1: goto_clicked (log_view); break;
413  case 2: save_to_file (GTK_WINDOW (dialog)); break;
414  default: gtk_widget_hide (GTK_WIDGET (dialog)); break;
415  }
416 }
417 
418 static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y,
419  gboolean keyboard_mode, GtkTooltip *tooltip, YGtkPkgListView *pThis)
420 {
421  GtkTreeView *view = GTK_TREE_VIEW (widget);
422  GtkTreeModel *model;
423  GtkTreePath *path;
424  GtkTreeIter iter;
425  if (gtk_tree_view_get_tooltip_context (view,
426  &x, &y, keyboard_mode, &model, &path, &iter)) {
427  gtk_tree_view_set_tooltip_row (view, tooltip, path);
428  gtk_tree_path_free (path);
429 
430  GtkTreeViewColumn *column;
431  int bx, by;
432  gtk_tree_view_convert_widget_to_bin_window_coords (
433  view, x, y, &bx, &by);
434  gtk_tree_view_get_path_at_pos (
435  view, x, y, NULL, &column, NULL, NULL);
436 
437  std::string text;
438  text.reserve (254);
439  const char *icon = 0;
440 
441  if (column == ygtk_tree_view_get_column (YGTK_TREE_VIEW (view), 2)) { // repository
442  char *name, *url;
443  gtk_tree_model_get (model, &iter, LogListHandler::REPOSITORY_COLUMN, &name,
444  LogListHandler::REPOSITORY_URL_COLUMN, &url, -1);
445  if (name && *name) {
446  text = name;
447  if (url && *url) {
448  text += "\n<small>"; text += url; text += "</small>";
449  icon = getRepositoryStockIcon (url);
450  }
451  if (url)
452  g_free (url);
453  }
454  if (name)
455  g_free (name);
456  }
457 
458  if (!text.empty()) {
459  gtk_tooltip_set_markup (tooltip, text.c_str());
460  gtk_tooltip_set_icon_from_icon_name (tooltip,
461  icon, GTK_ICON_SIZE_BUTTON);
462  return TRUE;
463  }
464  }
465  return FALSE;
466 }
467 
468 static void goto_activate_cb (GtkMenuItem *item, GtkTreeView *view)
469 { goto_clicked (view); }
470 
471 static void right_click_cb (YGtkTreeView *view, gboolean outreach)
472 {
473  GtkWidget *menu = gtk_menu_new();
474  if (!outreach) {
475  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
476  bool selected = gtk_tree_selection_get_selected (selection, NULL, NULL);
477 
478  GtkWidget *item = gtk_image_menu_item_new_from_stock (GTK_STOCK_JUMP_TO, NULL);
479  gtk_widget_set_sensitive (item, selected);
480  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
481  g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (goto_activate_cb), view);
482  }
483 
484  ygtk_tree_view_append_show_columns_item (view, menu);
485  ygtk_tree_view_popup_menu (view, menu);
486 }
487 
488 static gboolean read_logs_idle_cb (void *data)
489 {
490  GtkWidget **views = (GtkWidget **) data;
491  GtkWidget *dialog = views[0];
492  GtkTreeView *log_view = GTK_TREE_VIEW (views[1]);
493  GtkTreeView *date_view = GTK_TREE_VIEW (views[2]);
494 
495  ListHandler handler;
496  ZyppHistoryParser parser (&handler);
497 
498  gtk_tree_view_set_model (date_view, handler.date_handler->getModel());
499  gtk_tree_view_set_model (log_view, handler.log_handler->getModel());
500 
501  GtkTreeSelection *selection = gtk_tree_view_get_selection (date_view);
502  GtkTreeIter iter;
503  if (gtk_tree_model_get_iter_first (
504  gtk_tree_view_get_model (date_view), &iter))
505  gtk_tree_selection_select_iter (selection, &iter);
506 
507  gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(dialog)), NULL);
508  return FALSE;
509 }
510 
511 YGtkPkgHistoryDialog::YGtkPkgHistoryDialog()
512 {
513  GtkCellRenderer *renderer, *pix_renderer;
514  GtkTreeViewColumn *column;
515 
516  GtkWidget *log_view = ygtk_tree_view_new (_("No entries."));
517  gtk_tree_view_set_search_column (GTK_TREE_VIEW (log_view), LogListHandler::SHORTCUT_COLUMN);
518  gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (log_view), TRUE);
519  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (log_view), TRUE);
520  gtk_widget_set_has_tooltip (log_view, TRUE);
521  g_signal_connect (G_OBJECT (log_view), "query-tooltip",
522  G_CALLBACK (query_tooltip_cb), this);
523  g_signal_connect (G_OBJECT (log_view), "right-click",
524  G_CALLBACK (right_click_cb), this);
525 
526  bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL;
527 
528  column = gtk_tree_view_column_new();
529  gtk_tree_view_column_set_title (column, _("Action"));
530  gtk_tree_view_column_set_spacing (column, 6);
531 
532  pix_renderer = gtk_cell_renderer_pixbuf_new();
533  if (!reverse)
534  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
535 
536  renderer = ygtk_cell_renderer_text_new();
537  gtk_tree_view_column_pack_start (column, renderer, TRUE);
538  gtk_tree_view_column_set_attributes (column, renderer,
539  "markup", LogListHandler::NAME_COLUMN, "xpad", LogListHandler::XPAD_COLUMN,
540  "foreground", LogListHandler::COLOR_COLUMN, NULL);
541  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
542 
543  if (reverse)
544  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
545  gtk_tree_view_column_set_attributes (column, pix_renderer,
546  "icon-name", LogListHandler::ICON_COLUMN, NULL);
547 
548  gtk_tree_view_column_set_resizable (column, TRUE);
549  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
550  gtk_tree_view_column_set_expand (column, TRUE);
551  ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column);
552 
553  renderer = gtk_cell_renderer_text_new();
554  column = gtk_tree_view_column_new_with_attributes (_("Version / URL"), renderer,
555  "text", LogListHandler::VERSION_URL_COLUMN, "xpad", LogListHandler::XPAD_COLUMN,
556  "foreground", LogListHandler::COLOR_COLUMN, NULL);
557  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
558  gtk_tree_view_column_set_resizable (column, TRUE);
559  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
560  gtk_tree_view_column_set_fixed_width (column, 120);
561  ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column);
562 
563  column = gtk_tree_view_column_new();
564  gtk_tree_view_column_set_title (column, _("Repository"));
565  gtk_tree_view_column_set_spacing (column, 2);
566 
567  pix_renderer = gtk_cell_renderer_pixbuf_new();
568  if (!reverse)
569  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
570 
571  renderer = gtk_cell_renderer_text_new();
572  gtk_tree_view_column_pack_start (column, renderer, TRUE);
573  gtk_tree_view_column_set_attributes (column, renderer,
574  "text", LogListHandler::REPOSITORY_COLUMN, NULL);
575 
576  if (reverse)
577  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
578  gtk_tree_view_column_set_attributes (column, pix_renderer,
579  "icon-name", LogListHandler::REPOSITORY_ICON_COLUMN, NULL);
580 
581  gtk_tree_view_column_set_resizable (column, TRUE);
582  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
583  gtk_tree_view_column_set_fixed_width (column, 140);
584  gtk_tree_view_column_set_visible (column, FALSE);
585  ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column);
586 
587  renderer = gtk_cell_renderer_text_new();
588  column = gtk_tree_view_column_new_with_attributes (_("Requested by"), renderer,
589  "text", LogListHandler::REQBY_COLUMN, NULL);
590  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
591  gtk_tree_view_column_set_resizable (column, TRUE);
592  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
593  gtk_tree_view_column_set_fixed_width (column, 100);
594  gtk_tree_view_column_set_visible (column, FALSE);
595  ygtk_tree_view_append_column (YGTK_TREE_VIEW (log_view), column);
596 
597  GtkWidget *log_scroll = gtk_scrolled_window_new (NULL, NULL);
598  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (log_scroll),
599  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
600  gtk_scrolled_window_set_shadow_type (
601  GTK_SCROLLED_WINDOW (log_scroll), GTK_SHADOW_IN);
602  gtk_container_add (GTK_CONTAINER (log_scroll), log_view);
603 
604  GtkWidget *date_view = gtk_tree_view_new();
605  gtk_tree_view_set_search_column (GTK_TREE_VIEW (date_view), DateListHandler::TEXT_COLUMN);
606  gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (date_view), TRUE);
607 
608  renderer = gtk_cell_renderer_text_new();
609  column = gtk_tree_view_column_new_with_attributes (_("Date"), renderer,
610  "text", DateListHandler::TEXT_COLUMN, NULL);
611  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
612  gtk_tree_view_append_column (GTK_TREE_VIEW (date_view), column);
613 
614  GtkWidget *date_scroll = gtk_scrolled_window_new (NULL, NULL);
615  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (date_scroll),
616  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
617  gtk_scrolled_window_set_shadow_type (
618  GTK_SCROLLED_WINDOW (date_scroll), GTK_SHADOW_IN);
619  gtk_container_add (GTK_CONTAINER (date_scroll), date_view);
620 
621  GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(),
622  GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
623  _("Show History (%s)"), FILENAME);
624  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_JUMP_TO, 1);
625  gtk_dialog_add_button (GTK_DIALOG (dialog), _("Save to File"), 2);
626  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
627  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), 1, FALSE);
628  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
629  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
630  gtk_window_set_default_size (GTK_WINDOW (dialog), 650, 600);
631  g_signal_connect (G_OBJECT (dialog), "response",
632  G_CALLBACK (response_cb), log_view);
633  g_signal_connect (G_OBJECT (dialog), "delete-event",
634  G_CALLBACK (gtk_true), log_view);
635 
636  GtkWidget *hpaned = gtk_hpaned_new();
637  gtk_paned_pack1 (GTK_PANED (hpaned), date_scroll, FALSE, FALSE);
638  gtk_paned_pack2 (GTK_PANED (hpaned), log_scroll, TRUE, FALSE);
639  YGUtils::setPaneRelPosition (hpaned, .30);
640  gtk_widget_set_vexpand (hpaned, TRUE);
641  GtkContainer *content = GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog)));
642  gtk_container_add (content, hpaned);
643 
644  gtk_widget_show_all (dialog);
645  m_dialog = dialog;
646 
647  GtkTreeSelection *selection;
648  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (log_view));
649  g_signal_connect (G_OBJECT (selection), "changed",
650  G_CALLBACK (log_selection_changed_cb), dialog);
651  g_signal_connect (G_OBJECT (log_view), "row-activated",
652  G_CALLBACK (log_row_activated_cb), NULL);
653  gtk_tree_selection_set_select_function (selection, log_can_select_cb, NULL, NULL);
654  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (date_view));
655  g_signal_connect (G_OBJECT (selection), "changed",
656  G_CALLBACK (date_selection_changed_cb), log_view);
657 
658  GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
659  gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(dialog)), cursor);
660  gdk_cursor_unref (cursor);
661 
662  GtkWidget **views = g_new (GtkWidget *, 3);
663  views[0] = dialog; views[1] = log_view; views[2] = date_view;
664  g_idle_add_full (G_PRIORITY_LOW, read_logs_idle_cb, views, g_free);
665 }
666 
667 YGtkPkgHistoryDialog::~YGtkPkgHistoryDialog()
668 { gtk_widget_destroy (m_dialog); }
669 
670 void YGtkPkgHistoryDialog::popup()
671 { gtk_window_present (GTK_WINDOW (m_dialog)); }
672