Make find use the new DataView.
[gmpc.git] / src / Widgets / gmpc-data-view.vala
blobc3ff0e846fedda12c799ecb0b45e143d6d9212ba
1 using Gmpc;
3 using Gtk;
5 public List<string> paste_queue;
6 const string log_domain = "Gmpc.DataView";
8 /** The Default column width. */
9 const int default_column_width = 200;
11 /**
12 * List of columns.
13 * * List of column ids to show.
14 * * Name of each column
15 * * Default set of enabled columns.
17 const int NUM_COLS = 20;
18 const int[] gmpc_data_view_col_ids = {
19 Gmpc.MpdData.ColumnTypes.MARKUP,
20 Gmpc.MpdData.ColumnTypes.SONG_ARTIST, /* album name */
21 Gmpc.MpdData.ColumnTypes.SONG_ALBUM, /* album name */
22 Gmpc.MpdData.ColumnTypes.SONG_TITLE, /* song title */
23 Gmpc.MpdData.ColumnTypes.SONG_TITLEFILE, /* song title */
24 Gmpc.MpdData.ColumnTypes.SONG_GENRE, /* song genre */
25 Gmpc.MpdData.ColumnTypes.SONG_TRACK, /* song track */
26 Gmpc.MpdData.ColumnTypes.SONG_NAME, /* stream name */
27 Gmpc.MpdData.ColumnTypes.SONG_COMPOSER, /* composer name */
28 Gmpc.MpdData.ColumnTypes.SONG_PERFORMER, /* performer */
29 Gmpc.MpdData.ColumnTypes.SONG_DATE, /* date */
30 Gmpc.MpdData.ColumnTypes.SONG_LENGTH_FORMAT, /* length formatted */
31 Gmpc.MpdData.ColumnTypes.SONG_DISC, /* disc */
32 Gmpc.MpdData.ColumnTypes.SONG_COMMENT, /* comment */
33 Gmpc.MpdData.ColumnTypes.ICON_ID, /* icon id */
34 Gmpc.MpdData.ColumnTypes.SONG_POS,
35 Gmpc.MpdData.ColumnTypes.SONG_ALBUMARTIST,
36 Gmpc.MpdData.ColumnTypes.PATH_EXTENSION, /* Extension */
37 Gmpc.MpdData.ColumnTypes.PATH_DIRECTORY, /* Directory */
38 Gmpc.MpdData.ColumnTypes.SONG_PRIORITY
40 const string[] gmpc_data_view_col_names = {
41 N_("Markup"),
42 N_("Artist"),
43 N_("Album"),
44 N_("Title"),
45 N_("File"),
46 N_("Genre"),
47 N_("Track"),
48 N_("Name"),
49 N_("Composer"),
50 N_("Performer"),
51 N_("Date"),
52 N_("Duration"),
53 N_("Disc"),
54 N_("Comment"),
55 N_("Icon Id"),
56 N_("Position"),
57 N_("AlbumArtist"),
58 N_("Extension"),
59 N_("Directory"),
60 N_("Priority")
63 const bool[] gmpc_data_view_col_enabled = {
64 false,//"Markup",
65 true, //"Artist",
66 true,//"Album",
67 true,//"Title",
68 false,//"File",
69 false,//"Genre",
70 false,//"Track",
71 false,//"Name",
72 false,//"Composer",
73 false,//"Performer",
74 false,//"Date",
75 false,//"Duration",
76 false,//"Disc",
77 false,//"Comment",
78 true,//"Icon Id"
79 false,//"Position"
80 false,//"AlbumArtist"
81 false,//Extension
82 false,//Directory
83 false//Priority
87 const int[] gmpc_data_view_col_position = {
88 14,//"Markup",
89 3, //"Artist",
90 2,//"Album",
91 1,//"Title",
92 4,//"File",
93 5,//"Genre",
94 6,//"Track",
95 7,//"Name",
96 8,//"Composer",
97 9,//"Performer",
98 10,//"Date",
99 11,//"Duration",
100 12,//"Disc",
101 13,//"Comment",
102 0,//"Icon Id"
103 15, // "Position"
104 18, // "AlbumArtist"
105 16,// Extension
106 17, // Directory
110 public class Gmpc.DataView : Gtk.TreeView
112 private Gtk.TreeViewColumn[] tree_columns = new Gtk.TreeViewColumn[NUM_COLS];
114 * If we are a play-queue we should treat the content.
115 * slightly different.
116 * e.g. add-replace will be play-crop
118 public bool is_play_queue {get; set; default=false;}
121 * The name of the treeview.
122 * This is used to store the column layout.
124 public string uid {get; set; default="default";}
128 * Construction function.
130 public DataView(string name, bool play_queue = false)
132 log(log_domain, LogLevelFlags.LEVEL_INFO, "Constructing dataview: "+name);
133 this.is_play_queue = play_queue;
134 this.uid = name;
135 // Connect row activated signal.
136 this.row_activated.connect(__row_activated);
137 this.key_press_event.connect(__key_press_event_callback);
138 this.button_press_event.connect(__button_press_event_callback);
139 this.button_release_event.connect(__button_release_event_callback);
140 // When it getst he destroy signal.
141 this.destroy.connect(column_store_state);
143 this.set_rules_hint(true);
144 this.set_enable_search(false);
146 this.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
147 this.set_fixed_height_mode(true);
148 // Create the view.
149 column_populate();
154 * Deconstructor.
156 ~DataView()
161 public void right_mouse_menu(Gtk.Menu menu)
163 int selected_rows = this.get_selection().count_selected_rows();
164 if(selected_rows == 1) {
165 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.MEDIA_PLAY,null);
166 item.activate.connect((source)=>{
167 selected_songs_play();
169 menu.append(item);
171 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.INFO, null);
172 item.activate.connect((source)=>{
173 selected_songs_info();
175 menu.append(item);
178 if(is_play_queue)
180 // Add play if there is one selected row.
181 if(selected_rows > 0) {
182 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.REMOVE,null);
183 item.activate.connect((source)=>{
184 selected_songs_remove();
186 menu.append(item);
188 }else{
189 var item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.ADD,null);
190 item.activate.connect((source)=>{
191 selected_songs_add();
193 menu.append(item);
195 if(server.check_command_allowed("prioid") == MPD.Server.Command.ALLOWED)
197 menu.append( new Gtk.SeparatorMenuItem());
198 var item = new Gtk.MenuItem.with_label(_("Queue"));
199 item.activate.connect((source)=>{ selected_songs_raise_priority();});
200 menu.append(item);
202 if(is_play_queue) {
203 item = new Gtk.MenuItem.with_label(_("Dequeue"));
204 item.activate.connect((source)=>{ selected_songs_remove_priority();});
205 menu.append(item);
208 // Copy paste.
209 if(selected_rows > 0)
211 Gtk.MenuItem item;
212 item = new Gtk.SeparatorMenuItem();
213 menu.append(item);
214 if(is_play_queue) {
215 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.CUT, null);
216 item.activate.connect((source)=>{ selected_songs_paste_queue_cut();});
217 menu.append(item);
220 item = new Gtk.ImageMenuItem.from_stock(Gtk.Stock.COPY, null);
221 item.activate.connect((source)=>{ selected_songs_paste_queue_copy();});
222 menu.append(item);
224 if(is_play_queue && paste_queue != null) {
225 item = new Gtk.ImageMenuItem.with_label(_("Paste before"));
226 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
227 item.activate.connect((source)=>{ selected_songs_paste_before();});
228 menu.append(item);
230 item = new Gtk.ImageMenuItem.with_label(_("Paste after"));
231 (item as Gtk.ImageMenuItem).set_image(new Image.from_stock(Gtk.Stock.PASTE, Gtk.IconSize.MENU));
232 item.activate.connect((source)=>{ selected_songs_paste_after();});
233 menu.append(item);
237 if(selected_rows == 1)
239 Gtk.TreeModel model;
240 var list = get_selection().get_selected_rows(out model);
241 var path = list.first().data;
242 Gtk.TreeIter iter;
243 if(model.get_iter(out iter, path))
245 unowned MPD.Song? song;
246 model.get(iter, MpdData.ColumnTypes.MPDSONG, out song);
247 if(song != null){
248 MpdInteraction.submenu_for_song(menu, song);
253 // Tool menu integration.
254 bool initial = false;
255 var item = new Gtk.MenuItem.with_label(_("Tools"));
256 var smenu = new Gtk.Menu();
257 item.set_submenu(smenu);
258 for(int i = 0; i < Gmpc.num_plugins; i++){
259 if(plugins[i].get_enabled() &&
260 plugins[i].browser_song_list_option_menu(this,smenu) > 0){
261 stdout.printf("adding entry\n");
262 initial = true;
265 if(initial) {
266 __button_press_menu.append(new Gtk.SeparatorMenuItem());
267 __button_press_menu.append(item);
272 * Internal functions.
276 * Store the position, visibility and width of the columns
278 private void column_store_state()
280 // Save the position of the columns
281 var columns = get_columns();
282 int index = 0;
283 foreach(var column in columns)
285 int col_index = column.get_data("index");
286 int width = column.get_width();
287 config.set_int(uid+"-colpos", gmpc_data_view_col_names[col_index], index);
288 config.set_bool(uid+"-colshow", gmpc_data_view_col_names[col_index], column.visible);
289 // Only store width if bigger then 0.
290 if(width > 0 ) {
291 config.set_int(uid+"-colsize",gmpc_data_view_col_names[col_index], width);
293 index++;
296 // Hack to make vala not destroy the menu directly.
297 private Gtk.Menu column_selection_menu = null;
298 private void column_show_selection_menu()
300 column_selection_menu = new Gtk.Menu();
301 foreach(var col in tree_columns)
303 int index = col.get_data("index");
304 // Do not show the icon id in the selection list.
305 if(gmpc_data_view_col_ids[index] == MpdData.ColumnTypes.ICON_ID) continue;
306 var item = new Gtk.CheckMenuItem.with_label(FixGtk.gettext(gmpc_data_view_col_names[index]));
307 if(col.visible) {
308 item.set_active(true);
310 // On activation toggle the state.
311 item.activate.connect((source) => {
312 col.visible = (source as Gtk.CheckMenuItem).get_active();
314 column_selection_menu.append(item);
316 column_selection_menu.show_all();
317 column_selection_menu.popup(null, null, null, 0, Gtk.get_current_event_time());
320 * Populate the treeview with the right columns.
321 * The treeview should have a name now.
323 private void column_populate()
325 for(int i = 0; i < NUM_COLS; i++)
327 Gtk.TreeViewColumn col = new Gtk.TreeViewColumn();
328 col.set_data("index", i);
329 if(gmpc_data_view_col_ids[i] == Gmpc.MpdData.ColumnTypes.ICON_ID)
332 * Picture.
334 var renderer = new Gtk.CellRendererPixbuf();
335 renderer.xalign = 0.0f;
336 // Set up column
337 col.pack_start(renderer, true);
338 col.set_attributes(renderer, "icon-name", Gmpc.MpdData.ColumnTypes.ICON_ID);
339 col.set_resizable(false);
340 col.set_fixed_width(20);
341 col.clickable = true;
342 // If the user clicks on the column, show dropdown allowing to enable/disable columns.
343 col.clicked.connect((source) => {
344 column_show_selection_menu();
346 } else {
348 * Text column
350 col.set_title(FixGtk.gettext(gmpc_data_view_col_names[i]));
351 var renderer = new Gtk.CellRendererText();
352 renderer.ellipsize = Pango.EllipsizeMode.END;
353 // Set up column
354 if(is_play_queue) {
355 renderer.weight_set = true;
356 renderer.style_set = true;
357 col.set_cell_data_func(renderer, highlight_row_current_song_playing);
359 col.pack_start(renderer, true);
360 col.set_attributes(renderer, "text", gmpc_data_view_col_ids[i]);
361 col.set_resizable(true);
363 int width = config.get_int_with_default(uid+"-colsize",gmpc_data_view_col_names[i], default_column_width);
364 // Do not set to size 0, then revert back to 200.
365 col.set_fixed_width(width>0?width:default_column_width);
367 col.set_sizing(Gtk.TreeViewColumnSizing.FIXED);
368 col.set_reorderable(true);
370 // Fixed width.
371 int pos = config.get_int_with_default(uid+"-colpos", gmpc_data_view_col_names[i], gmpc_data_view_col_position[i]);
372 this.tree_columns[pos] = col;
374 // Add the columns (in right order)
375 for(int i = 0; i < NUM_COLS; i++) {
376 int index = this.tree_columns[i].get_data("index");
377 this.insert_column(this.tree_columns[i], i);
378 this.tree_columns[i].set_visible(config.get_bool_with_default(uid+"-colshow", gmpc_data_view_col_names[index], gmpc_data_view_col_enabled[index]));
387 * Function handles the row-activate signal.
389 private void __row_activated (Gtk.TreePath path, Gtk.TreeViewColumn col)
391 Gtk.TreeModel? model = this.get_model();
392 if(model != null)
394 Gtk.TreeIter iter;
395 if(!model.get_iter(out iter, path)) return;
396 if(is_play_queue) {
397 /* If we are play-queue, play the selected song. */
398 int song_id;
399 model.get(iter, Gmpc.MpdData.ColumnTypes.SONG_ID, out song_id);
400 MPD.Player.play_id(Gmpc.server, song_id);
401 } else {
402 /* If we are a song browser, add the path and play it. */
403 string song_path;
404 model.get(iter, Gmpc.MpdData.ColumnTypes.PATH, out song_path);
405 MpdInteraction.play_path(song_path);
410 * Check if current row is playing.
412 private void highlight_row_current_song_playing(
413 Gtk.CellLayout col,
414 Gtk.CellRenderer renderer,
415 Gtk.TreeModel model,
416 Gtk.TreeIter iter)
418 // The current song we make bold.
419 if(model is Gmpc.MpdData.ModelPlaylist &&
420 (model as Gmpc.MpdData.ModelPlaylist).is_current_song(iter)){
421 (renderer as Gtk.CellRendererText).weight = Pango.Weight.BOLD;
422 }else{
423 (renderer as Gtk.CellRendererText).weight = Pango.Weight.NORMAL;
426 // A prioritized song we make italic.
427 int prio = 0;
428 model.get(iter, Gmpc.MpdData.ColumnTypes.SONG_PRIORITY, out prio);
429 if(prio > 0) {
430 (renderer as Gtk.CellRendererText).style = Pango.Style.ITALIC;
431 }else {
432 (renderer as Gtk.CellRendererText).style = Pango.Style.NORMAL;
438 * Handle keyboard input.
440 private bool __key_press_event_callback_song_list(Gdk.EventKey event)
442 if(event.keyval == Gdk.Key_i)
444 return selected_songs_add();
446 else if (event.keyval == Gdk.Key_r)
450 return false;
452 private bool __key_press_event_callback_play_queue(Gdk.EventKey event)
454 if (event.keyval == Gdk.Key_Q)
456 // remove priority.
457 return selected_songs_remove_priority();
459 else if (event.keyval == Gdk.Key_c)
461 // Cut (if available) into clipboard
462 selected_songs_paste_queue_cut();
464 else if (event.keyval == Gdk.Key_P)
466 // Paste before
467 return selected_songs_paste_before();
469 else if (event.keyval == Gdk.Key_p)
471 // Paste after
472 return selected_songs_paste_after();
474 else if (event.keyval == Gdk.Key_d)
476 if(!selected_songs_remove())
478 // Detach model (for some reason keeping it attached
479 // Makes thing break, work-around for now)
480 // TODO: fixme
481 var model = get_model();
482 this.model = null;
483 // Clear
484 MPD.PlayQueue.clear(server);
485 // Re-add model
486 this.model = model;
487 return true;
490 return false;
492 private bool __key_press_event_callback(Gdk.EventKey event)
494 if(event.keyval == Gdk.Key_j)
496 // Move cursor down.
497 move_cursor_down();
499 else if (event.keyval == Gdk.Key_k)
501 // Move cursor up
502 move_cursor_up();
504 else if(event.keyval == Gdk.Key_y)
506 // Copy data to clipboard
507 return selected_songs_paste_queue_copy();
509 else if (event.keyval == Gdk.Key_o)
511 return selected_songs_info();
513 else if (event.keyval == Gdk.Key_Escape)
517 else if (event.keyval == Gdk.Key_m)
519 // Configure columns
520 column_show_selection_menu();
521 return true;
523 else if (event.keyval == Gdk.Key_q)
525 // Raise priority.
526 return selected_songs_raise_priority();
528 else if (event.keyval == Gdk.Key_Menu)
530 __button_press_menu = new Gtk.Menu();
532 right_mouse_menu(__button_press_menu);
537 __button_press_menu.show_all();
538 __button_press_menu.popup(null, null,null,0, Gtk.get_current_event_time());
539 return true;
542 // Commands specific to play_queue
543 if(is_play_queue)
545 if(__key_press_event_callback_play_queue(event)) return true;
547 else
549 if(__key_press_event_callback_song_list(event)) return true;
551 return false;
555 * Right mouse popup.
557 // Hack to stop vala from destroying my menu.
558 private Gtk.Menu __button_press_menu = null;
559 private bool __button_press_event_callback(Gdk.EventButton event)
561 if(event.button == 3)
563 __button_press_menu = new Gtk.Menu();
564 right_mouse_menu(__button_press_menu);
566 __button_press_menu.show_all();
567 __button_press_menu.popup(null, null,null, event.button, event.time);
568 return true;
570 return false;
572 private bool __button_release_event_callback(Gdk.EventButton event)
574 return false;
579 * Interaction on selected songs.
581 // Set priority on the selected songs.
582 private bool selected_songs_raise_priority()
584 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
585 var selection = this.get_selection();
587 if(selection.count_selected_rows() > 254) {
588 Gmpc.Messages.show(_("You can only queue 254 songs at the time."), Gmpc.Messages.Level.WARNING);
589 return false;
592 int priority = 255;
593 Gtk.TreeModel model;
594 foreach(var path in selection.get_selected_rows(out model))
596 Gtk.TreeIter iter;
597 if(model.get_iter(out iter, path))
599 int song_id;
600 model.get(iter,Gmpc.MpdData.ColumnTypes.SONG_ID, out song_id);
601 if(song_id >= 0) {
602 MPD.PlayQueue.set_priority(server, song_id, priority--);
603 } else {
604 string song_path;
605 model.get(iter, Gmpc.MpdData.ColumnTypes.PATH, out song_path);
606 song_id = MPD.PlayQueue.add_song_get_id(server,song_path);
607 MPD.PlayQueue.set_priority(server, song_id, priority--);
611 return true;
613 // Remove the set priority from the selected songs.
614 private bool selected_songs_remove_priority()
616 if(server.check_command_allowed("prioid") != MPD.Server.Command.ALLOWED) return false;
617 var selection = this.get_selection();
619 Gtk.TreeModel model;
620 foreach(var path in selection.get_selected_rows(out model))
622 Gtk.TreeIter iter;
623 if(model.get_iter(out iter, path))
625 int song_id;
626 model.get(iter,Gmpc.MpdData.ColumnTypes.SONG_ID, out song_id);
627 MPD.PlayQueue.set_priority(server, song_id, 0);
630 return true;
632 // Add the selected song
633 private bool selected_songs_add()
635 int added_rows = 0;
636 var selection = this.get_selection();
638 Gtk.TreeModel model;
639 foreach(var path in selection.get_selected_rows(out model))
641 Gtk.TreeIter iter;
642 if(model.get_iter(out iter, path))
644 string song_path;
645 model.get(iter,Gmpc.MpdData.ColumnTypes.PATH, out song_path);
646 MPD.PlayQueue.queue_add_song(server, song_path);
647 added_rows++;
650 MPD.PlayQueue.queue_commit(server);
651 if(added_rows > 0) {
652 return true;
654 return false;
656 // Play the selected song
657 private bool selected_songs_play()
659 var selection = this.get_selection();
660 Gtk.TreeModel model;
661 foreach(var path in selection.get_selected_rows(out model))
663 Gtk.TreeIter iter;
664 if(model.get_iter(out iter, path))
666 if(is_play_queue) {
667 int song_id;
668 model.get(iter, Gmpc.MpdData.ColumnTypes.SONG_ID, out song_id);
669 if(song_id >= 0){
670 MPD.Player.play_id(server, song_id);
671 return true;
673 }else{
674 string song_path;
675 model.get(iter, Gmpc.MpdData.ColumnTypes.PATH, out song_path);
676 MpdInteraction.play_path(song_path);
677 return true;
681 return selection.count_selected_rows() > 0;
683 // Remove the selected songs from the play queue.
684 private bool selected_songs_remove()
686 int deleted_rows = 0;
687 var selection = this.get_selection();
689 Gtk.TreeModel model;
690 foreach(var path in selection.get_selected_rows(out model))
692 Gtk.TreeIter iter;
693 if(model.get_iter(out iter, path))
695 int song_id;
696 model.get(iter,Gmpc.MpdData.ColumnTypes.SONG_ID, out song_id);
697 MPD.PlayQueue.queue_delete_id(server, song_id);
698 deleted_rows++;
701 MPD.PlayQueue.queue_commit(server);
702 if(deleted_rows > 0) {
703 selection.unselect_all();
704 return true;
706 return false;
708 // Show the Information of the first selected song.
709 private bool selected_songs_info()
711 var selection = this.get_selection();
712 Gtk.TreeModel model;
713 foreach(var path in selection.get_selected_rows(out model))
715 Gtk.TreeIter iter;
716 if(model.get_iter(out iter, path))
718 unowned MPD.Song? song = null;
719 model.get(iter, Gmpc.MpdData.ColumnTypes.MPDSONG, out song);
720 if(song != null) {
721 Browser.Metadata.show();
722 Browser.Metadata.show_song(song);
723 return true;
727 return selection.count_selected_rows() > 0;
730 // Cut the selected songs in the play queue
731 private bool selected_songs_paste_queue_cut()
733 if(selected_songs_paste_queue_copy()){
734 selected_songs_remove();
736 return false;
738 // Copy the selected songs in the play queue
739 private bool selected_songs_paste_queue_copy()
741 var selection = this.get_selection();
742 // If nothing to copy , do nothing.
743 if(selection.count_selected_rows() == 0) return false;
744 // Clear paste_queue
745 paste_queue = null;
746 Gtk.TreeModel model;
747 foreach(var path in selection.get_selected_rows(out model))
749 Gtk.TreeIter iter;
750 if(model.get_iter(out iter, path))
752 string insert_path;
753 model.get(iter,Gmpc.MpdData.ColumnTypes.PATH, out insert_path);
754 paste_queue.prepend(insert_path);
757 paste_queue.reverse();
758 return true;
762 private bool selected_songs_paste_before()
764 var selection = this.get_selection();
765 // If nothing selected.
766 if(selection.count_selected_rows() == 0|| paste_queue == null)
768 return false;
770 Gtk.TreeModel model;
771 Gtk.TreeIter iter;
772 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
773 if(model.get_iter(out iter, path))
775 int songpos;
776 model.get(iter, Gmpc.MpdData.ColumnTypes.SONG_POS, out songpos);
777 if(songpos> 0) {
778 songpos--;
779 paste_queue.reverse();
780 foreach(var fpath in paste_queue)
782 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
783 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
785 paste_queue.reverse();
788 return true;
790 private bool selected_songs_paste_after()
792 var selection = this.get_selection();
793 // If nothing selected.
794 if(selection.count_selected_rows() == 0|| paste_queue == null)
796 return false;
798 Gtk.TreeModel model;
799 Gtk.TreeIter iter;
800 Gtk.TreePath path = selection.get_selected_rows(out model).last().data;
801 if(model.get_iter(out iter, path))
803 int songpos;
804 model.get(iter, Gmpc.MpdData.ColumnTypes.SONG_POS, out songpos);
805 if(songpos> 0) {
806 paste_queue.reverse();
807 foreach(var fpath in paste_queue)
809 int nsongid = MPD.PlayQueue.add_song_get_id(server,fpath);
810 MPD.PlayQueue.song_move_id(server, nsongid, songpos);
812 paste_queue.reverse();
815 return true;
819 * Move
821 private void move_cursor_down()
823 Gtk.TreePath? path;
824 Gtk.TreeViewColumn? col;
825 this.get_cursor(out path, out col);
826 if(path != null)
828 Gtk.TreeIter iter;
829 path.next();
830 if(this.model.get_iter(out iter, path))
832 this.set_cursor(path, col, false);
836 private void move_cursor_up()
838 Gtk.TreePath? path;
839 Gtk.TreeViewColumn? col;
840 this.get_cursor(out path, out col);
841 if(path != null)
843 if(path.prev())
845 this.set_cursor(path, col, false);