1 #include <Elementary.h>
5 typedef struct _Widget_Data Widget_Data;
6 typedef struct _Elm_Spinner_Special_Value Elm_Spinner_Special_Value;
10 Evas_Object *spinner, *ent;
13 double val, val_min, val_max, orig_val, step;
14 double drag_start_pos, spin_speed, interval, first_interval;
16 double val, val_min, val_max, orig_val, step, base;
17 double drag_start_pos, spin_speed, interval, first_interval;
19 >>>>>>> remotes/origin/upstream
20 Ecore_Timer *delay, *spin;
21 Eina_List *special_values;
23 Eina_Bool entry_visible : 1;
24 Eina_Bool dragging : 1;
25 Eina_Bool editable : 1;
28 struct _Elm_Spinner_Special_Value
34 static const char *widtype = NULL;
35 static void _del_hook(Evas_Object *obj);
36 static void _disable_hook(Evas_Object *obj);
37 static void _write_label(Evas_Object *obj);
38 static void _sizing_eval(Evas_Object *obj);
39 //static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
40 static Eina_Bool _value_set(Evas_Object *obj, double delta);
41 static void _on_focus_hook(void *data, Evas_Object *obj);
42 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
43 Evas_Callback_Type type, void *event_info);
45 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
47 static const char SIG_CHANGED[] = "changed";
48 static const char SIG_DELAY_CHANGED[] = "delay,changed";
50 static const Evas_Smart_Cb_Description _signals[] = {
52 {SIG_DELAY_CHANGED, ""},
57 _del_hook(Evas_Object *obj)
59 Elm_Spinner_Special_Value *sv;
60 Widget_Data *wd = elm_widget_data_get(obj);
62 if (wd->label) eina_stringshare_del(wd->label);
63 if (wd->delay) ecore_timer_del(wd->delay);
64 if (wd->spin) ecore_timer_del(wd->spin);
65 if (wd->special_values)
67 EINA_LIST_FREE(wd->special_values, sv)
69 eina_stringshare_del(sv->label);
77 _disable_hook(Evas_Object *obj)
79 Widget_Data *wd = elm_widget_data_get(obj);
81 if (elm_widget_disabled_get(obj))
82 edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
84 edje_object_signal_emit(wd->spinner, "elm,state,enabled", "elm");
88 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
90 Widget_Data *wd = elm_widget_data_get(obj);
92 edje_object_signal_emit(wd->spinner, emission, source);
96 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
98 Widget_Data *wd = elm_widget_data_get(obj);
100 edje_object_signal_callback_add(wd->spinner, emission,
101 source, func_cb, data);
105 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
107 Widget_Data *wd = elm_widget_data_get(obj);
108 edje_object_signal_callback_del_full(wd->spinner, emission, source,
113 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
115 Widget_Data *wd = elm_widget_data_get(obj);
117 edje_object_mirrored_set(wd->spinner, rtl);
121 _theme_hook(Evas_Object *obj)
123 Widget_Data *wd = elm_widget_data_get(obj);
125 _elm_widget_mirrored_reload(obj);
126 _mirrored_set(obj, elm_widget_mirrored_get(obj));
127 _elm_theme_object_set(obj, wd->spinner, "spinner", "base", elm_widget_style_get(obj));
128 edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
130 if (elm_widget_focus_get(obj))
131 edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
133 edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
134 if (elm_widget_disabled_get(obj))
135 edje_object_signal_emit(wd->spinner, "elm,state,disabled", "elm");
136 edje_object_message_signal_process(wd->spinner);
137 edje_object_scale_set(wd->spinner, elm_widget_scale_get(obj) * _elm_config->scale);
142 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
144 Widget_Data *wd = elm_widget_data_get(obj);
146 if (elm_widget_focus_get(obj))
148 edje_object_signal_emit(wd->spinner, "elm,action,focus", "elm");
149 evas_object_focus_set(wd->spinner, EINA_TRUE);
153 edje_object_signal_emit(wd->spinner, "elm,action,unfocus", "elm");
154 evas_object_focus_set(wd->spinner, EINA_FALSE);
159 _delay_change(void *data)
161 Widget_Data *wd = elm_widget_data_get(data);
162 if (!wd) return ECORE_CALLBACK_CANCEL;
164 evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
165 return ECORE_CALLBACK_CANCEL;
169 _entry_show(Widget_Data *wd)
171 char buf[32], fmt[32] = "%0.f";
173 /* try to construct just the format from given label
174 * completely ignoring pre/post words
178 const char *start = strchr(wd->label, '%');
185 start = strchr(start + 2, '%');
190 const char *itr, *end = NULL;
191 for (itr = start + 1; *itr != '\0'; itr++)
193 /* allowing '%d' is quite dangerous, remove it? */
194 if ((*itr == 'd') || (*itr == 'f'))
201 if ((end) && ((size_t)(end - start + 1) < sizeof(fmt)))
203 memcpy(fmt, start, end - start);
204 fmt[end - start] = '\0';
208 snprintf(buf, sizeof(buf), fmt, wd->val);
209 elm_object_text_set(wd->ent, buf);
213 _write_label(Evas_Object *obj)
216 Elm_Spinner_Special_Value *sv;
217 Widget_Data *wd = elm_widget_data_get(obj);
222 >>>>>>> remotes/origin/upstream
224 EINA_LIST_FOREACH(wd->special_values, l, sv)
226 if (sv->value == wd->val)
228 snprintf(buf, sizeof(buf), "%s", sv->label);
233 snprintf(buf, sizeof(buf), wd->label, wd->val);
235 snprintf(buf, sizeof(buf), "%.0f", wd->val);
238 edje_object_part_text_set(wd->spinner, "elm.text", buf);
239 if (wd->entry_visible) _entry_show(wd);
244 _value_set(Evas_Object *obj, double delta)
246 Widget_Data *wd = elm_widget_data_get(obj);
248 if (!wd) return EINA_FALSE;
249 new_val = wd->val + delta;
251 _value_set(Evas_Object *obj, double new_val)
253 Widget_Data *wd = elm_widget_data_get(obj);
255 if (!wd) return EINA_FALSE;
259 (double)((((int)(new_val - wd->base)) / wd->round) * wd->round);
261 >>>>>>> remotes/origin/upstream
264 while (new_val < wd->val_min)
265 new_val = wd->val_max + new_val + 1 - wd->val_min;
266 while (new_val > wd->val_max)
267 new_val = wd->val_min + new_val - wd->val_max - 1;
271 if (new_val < wd->val_min)
272 new_val = wd->val_min;
273 else if (new_val > wd->val_max)
274 new_val = wd->val_max;
277 if (new_val == wd->val) return EINA_FALSE;
280 evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
281 if (wd->delay) ecore_timer_del(wd->delay);
282 wd->delay = ecore_timer_add(0.2, _delay_change, obj);
288 _sizing_eval(Evas_Object *obj)
290 Widget_Data *wd = elm_widget_data_get(obj);
291 Evas_Coord minw = -1, minh = -1;
293 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
294 edje_object_size_min_restricted_calc(wd->spinner, &minw, &minh, minw, minh);
295 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
296 evas_object_size_hint_min_set(obj, minw, minh);
297 evas_object_size_hint_max_set(obj, -1, -1);
302 _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info)
309 _val_set(Evas_Object *obj)
311 Widget_Data *wd = elm_widget_data_get(obj);
314 if (wd->val_max > wd->val_min)
315 pos = ((wd->val - wd->val_min) / (wd->val_max - wd->val_min));
316 if (pos < 0.0) pos = 0.0;
317 else if (pos > 1.0) pos = 1.0;
318 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
323 _drag(void *data, Evas_Object *_obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
325 Evas_Object *obj = data;
326 Widget_Data *wd = elm_widget_data_get(obj);
327 double pos = 0.0, offset, delta;
329 if (wd->entry_visible) return;
330 edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
334 delta = (pos - wd->drag_start_pos) * offset;
335 /* If we are on rtl mode, change the delta to be negative on such changes */
336 if (elm_widget_mirrored_get(obj))
338 if (_value_set(data, delta)) _write_label(data);
339 wd->drag_start_pos = pos;
342 offset = wd->step * _elm_config->scale;
343 delta = (pos - wd->drag_start_pos) * offset;
344 /* If we are on rtl mode, change the delta to be negative on such changes */
345 if (elm_widget_mirrored_get(obj)) delta *= -1;
346 if (_value_set(data, wd->drag_start_pos + delta)) _write_label(data);
347 >>>>>>> remotes/origin/upstream
352 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
354 Widget_Data *wd = elm_widget_data_get(data);
357 edje_object_part_drag_value_get(wd->spinner, "elm.dragable.slider",
359 wd->drag_start_pos = pos;
363 _drag_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
365 Widget_Data *wd = elm_widget_data_get(data);
367 wd->drag_start_pos = 0;
368 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider", 0.0, 0.0);
372 _hide_entry(Evas_Object *obj)
374 Widget_Data *wd = elm_widget_data_get(obj);
376 edje_object_signal_emit(wd->spinner, "elm,state,inactive", "elm");
377 wd->entry_visible = 0;
381 _reset_value(Evas_Object *obj)
383 Widget_Data *wd = elm_widget_data_get(obj);
386 elm_spinner_value_set(obj, wd->orig_val);
390 _apply_entry_value(Evas_Object *obj)
392 Widget_Data *wd = elm_widget_data_get(obj);
399 str = elm_object_text_get(wd->ent);
401 val = strtod(str, &end);
402 if ((*end != '\0') && (!isspace(*end))) return;
403 elm_spinner_value_set(obj, val);
407 _toggle_entry(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
409 Widget_Data *wd = elm_widget_data_get(data);
416 if (elm_widget_disabled_get(data)) return;
417 if (!wd->editable) return;
418 if (wd->entry_visible) _apply_entry_value(data);
421 wd->orig_val = wd->val;
422 edje_object_signal_emit(wd->spinner, "elm,state,active", "elm");
424 elm_entry_select_all(wd->ent);
425 elm_widget_focus_set(wd->ent, 1);
426 wd->entry_visible = 1;
431 _spin_value(void *data)
433 Widget_Data *wd = elm_widget_data_get(data);
434 if (!wd) return ECORE_CALLBACK_CANCEL;
436 if (_value_set(data, wd->spin_speed)) _write_label(data);
438 if (_value_set(data, wd->val + wd->spin_speed)) _write_label(data);
439 >>>>>>> remotes/origin/upstream
440 wd->interval = wd->interval / 1.05;
441 ecore_timer_interval_set(wd->spin, wd->interval);
442 return ECORE_CALLBACK_RENEW;
446 _val_inc_start(Evas_Object *obj)
448 Widget_Data *wd = elm_widget_data_get(obj);
450 wd->interval = wd->first_interval;
451 wd->spin_speed = wd->step;
452 if (wd->spin) ecore_timer_del(wd->spin);
453 wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
458 _val_inc_stop(Evas_Object *obj)
460 Widget_Data *wd = elm_widget_data_get(obj);
462 wd->interval = wd->first_interval;
464 if (wd->spin) ecore_timer_del(wd->spin);
469 _val_dec_start(Evas_Object *obj)
471 Widget_Data *wd = elm_widget_data_get(obj);
473 wd->interval = wd->first_interval;
474 wd->spin_speed = -wd->step;
475 if (wd->spin) ecore_timer_del(wd->spin);
476 wd->spin = ecore_timer_add(wd->interval, _spin_value, obj);
481 _val_dec_stop(Evas_Object *obj)
483 Widget_Data *wd = elm_widget_data_get(obj);
485 wd->interval = wd->first_interval;
487 if (wd->spin) ecore_timer_del(wd->spin);
492 _button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
494 Widget_Data *wd = elm_widget_data_get(data);
496 if (wd->entry_visible)
501 _val_inc_start(data);
505 _button_inc_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
507 Widget_Data *wd = elm_widget_data_get(data);
513 _button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
515 Widget_Data *wd = elm_widget_data_get(data);
517 if (wd->entry_visible)
522 _val_dec_start(data);
526 _button_dec_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
528 Widget_Data *wd = elm_widget_data_get(data);
534 _entry_activated(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
536 Widget_Data *wd = elm_widget_data_get(data);
538 _apply_entry_value(data);
539 evas_object_smart_callback_call(data, SIG_CHANGED, NULL);
540 if (wd->delay) ecore_timer_del(wd->delay);
541 wd->delay = ecore_timer_add(0.2, _delay_change, data);
545 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
547 Widget_Data *wd = elm_widget_data_get(obj);
548 if (!wd) return EINA_FALSE;
549 if (elm_widget_disabled_get(obj)) return EINA_FALSE;
550 if (type == EVAS_CALLBACK_KEY_DOWN)
552 Evas_Event_Key_Down *ev = event_info;
553 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
554 else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
555 || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
558 edje_object_signal_emit(wd->spinner, "elm,left,anim,activate", "elm");
559 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
562 else if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
563 || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
566 edje_object_signal_emit(wd->spinner, "elm,right,anim,activate", "elm");
567 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
571 else if (type == EVAS_CALLBACK_KEY_UP)
573 Evas_Event_Key_Down *ev = event_info;
574 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
575 if (!strcmp(ev->keyname, "Right") || !strcmp(ev->keyname, "KP_Right")
576 || !strcmp(ev->keyname, "Up") || !strcmp(ev->keyname, "KP_Up"))
578 else if (!strcmp(ev->keyname, "Left") || !strcmp(ev->keyname, "KP_Left")
579 || !strcmp(ev->keyname, "Down") || !strcmp(ev->keyname, "KP_Down"))
581 else return EINA_FALSE;
582 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
589 elm_spinner_add(Evas_Object *parent)
595 ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
597 ELM_SET_WIDTYPE(widtype, "spinner");
598 elm_widget_type_set(obj, "spinner");
599 elm_widget_sub_object_add(parent, obj);
600 elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
601 elm_widget_data_set(obj, wd);
602 elm_widget_del_hook_set(obj, _del_hook);
603 elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
604 elm_widget_theme_hook_set(obj, _theme_hook);
605 elm_widget_disable_hook_set(obj, _disable_hook);
606 elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
607 elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
608 elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
609 elm_widget_can_focus_set(obj, EINA_TRUE);
610 elm_widget_event_hook_set(obj, _event_hook);
617 wd->first_interval = 0.85;
618 wd->entry_visible = 0;
619 wd->editable = EINA_TRUE;
621 wd->spinner = edje_object_add(e);
622 _elm_theme_object_set(obj, wd->spinner, "spinner", "base", "default");
623 elm_widget_resize_object_set(obj, wd->spinner);
624 edje_object_signal_callback_add(wd->spinner, "drag", "*", _drag, obj);
625 edje_object_signal_callback_add(wd->spinner, "drag,start", "*",
627 edje_object_signal_callback_add(wd->spinner, "drag,stop", "*",
629 edje_object_signal_callback_add(wd->spinner, "drag,step", "*",
631 edje_object_signal_callback_add(wd->spinner, "drag,page", "*",
634 edje_object_signal_callback_add(wd->spinner, "elm,action,increment,start",
635 "*", _button_inc_start, obj);
636 edje_object_signal_callback_add(wd->spinner, "elm,action,increment,stop",
637 "*", _button_inc_stop, obj);
638 edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,start",
639 "*", _button_dec_start, obj);
640 edje_object_signal_callback_add(wd->spinner, "elm,action,decrement,stop",
641 "*", _button_dec_stop, obj);
642 edje_object_part_drag_value_set(wd->spinner, "elm.dragable.slider",
645 wd->ent = elm_entry_add(obj);
646 elm_entry_single_line_set(wd->ent, 1);
647 evas_object_smart_callback_add(wd->ent, "activated", _entry_activated, obj);
648 edje_object_part_swallow(wd->spinner, "elm.swallow.entry", wd->ent);
649 edje_object_signal_callback_add(wd->spinner, "elm,action,entry,toggle",
650 "*", _toggle_entry, obj);
652 evas_object_smart_callbacks_descriptions_set(obj, _signals);
654 _mirrored_set(obj, elm_widget_mirrored_get(obj));
661 elm_spinner_label_format_set(Evas_Object *obj, const char *fmt)
663 ELM_CHECK_WIDTYPE(obj, widtype);
664 Widget_Data *wd = elm_widget_data_get(obj);
666 eina_stringshare_replace(&wd->label, fmt);
672 elm_spinner_label_format_get(const Evas_Object *obj)
674 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
675 Widget_Data *wd = elm_widget_data_get(obj);
676 if (!wd) return NULL;
681 elm_spinner_min_max_set(Evas_Object *obj, double min, double max)
683 ELM_CHECK_WIDTYPE(obj, widtype);
684 Widget_Data *wd = elm_widget_data_get(obj);
686 if ((wd->val_min == min) && (wd->val_max == max)) return;
689 if (wd->val < wd->val_min) wd->val = wd->val_min;
690 if (wd->val > wd->val_max) wd->val = wd->val_max;
696 elm_spinner_min_max_get(const Evas_Object *obj, double *min, double *max)
700 ELM_CHECK_WIDTYPE(obj, widtype);
701 Widget_Data *wd = elm_widget_data_get(obj);
703 if (min) *min = wd->val_min;
704 if (max) *max = wd->val_max;
708 elm_spinner_step_set(Evas_Object *obj, double step)
710 ELM_CHECK_WIDTYPE(obj, widtype);
711 Widget_Data *wd = elm_widget_data_get(obj);
717 elm_spinner_step_get(const Evas_Object *obj)
719 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
720 Widget_Data *wd = elm_widget_data_get(obj);
726 elm_spinner_value_set(Evas_Object *obj, double val)
728 ELM_CHECK_WIDTYPE(obj, widtype);
729 Widget_Data *wd = elm_widget_data_get(obj);
731 if (wd->val == val) return;
733 if (wd->val < wd->val_min) wd->val = wd->val_min;
734 if (wd->val > wd->val_max) wd->val = wd->val_max;
740 elm_spinner_value_get(const Evas_Object *obj)
742 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
743 Widget_Data *wd = elm_widget_data_get(obj);
749 elm_spinner_wrap_set(Evas_Object *obj, Eina_Bool wrap)
751 ELM_CHECK_WIDTYPE(obj, widtype);
752 Widget_Data *wd = elm_widget_data_get(obj);
758 elm_spinner_wrap_get(const Evas_Object *obj)
760 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
761 Widget_Data *wd = elm_widget_data_get(obj);
762 if (!wd) return EINA_FALSE;
767 elm_spinner_special_value_add(Evas_Object *obj, double value, const char *label)
769 Elm_Spinner_Special_Value *sv;
770 ELM_CHECK_WIDTYPE(obj, widtype);
771 Widget_Data *wd = elm_widget_data_get(obj);
774 sv = calloc(1, sizeof(*sv));
777 sv->label = eina_stringshare_add(label);
779 wd->special_values = eina_list_append(wd->special_values, sv);
784 elm_spinner_editable_set(Evas_Object *obj, Eina_Bool editable)
786 ELM_CHECK_WIDTYPE(obj, widtype);
787 Widget_Data *wd = elm_widget_data_get(obj);
789 wd->editable = editable;
793 elm_spinner_editable_get(const Evas_Object *obj)
795 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
796 Widget_Data *wd = elm_widget_data_get(obj);
797 if (!wd) return EINA_FALSE;
802 elm_spinner_interval_set(Evas_Object *obj, double interval)
804 ELM_CHECK_WIDTYPE(obj, widtype);
805 Widget_Data *wd = elm_widget_data_get(obj);
807 wd->first_interval = interval;
811 elm_spinner_interval_get(const Evas_Object *obj)
813 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
814 Widget_Data *wd = elm_widget_data_get(obj);
816 return wd->first_interval;
822 elm_spinner_base_set(Evas_Object *obj, double base)
824 ELM_CHECK_WIDTYPE(obj, widtype);
825 Widget_Data *wd = elm_widget_data_get(obj);
831 elm_spinner_base_get(const Evas_Object *obj)
833 ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
834 Widget_Data *wd = elm_widget_data_get(obj);
840 elm_spinner_round_set(Evas_Object *obj, int rnd)
842 ELM_CHECK_WIDTYPE(obj, widtype);
843 Widget_Data *wd = elm_widget_data_get(obj);
849 elm_spinner_round_get(const Evas_Object *obj)
851 ELM_CHECK_WIDTYPE(obj, widtype) 0;
852 Widget_Data *wd = elm_widget_data_get(obj);
856 >>>>>>> remotes/origin/upstream